javascript - Javascript异步/等待行为说明

标签 javascript async-await es6-promise

我一直以为Javascipt的async / await只是糖语法,例如编写这种和平的代码

let asyncVar = await AsyncFunc()
console.log(asyncVar)
//rest of code with asyncVar


相当于写这个

AsyncFunc().then(asyncVar => {
   console.log(asyncVar)
   //rest of the code with asyncVar
})


特别是因为您可以在某个随机的非承诺对象上调用await,它将尝试调用then()函数

但是,我尝试了这种代码的安宁

let asyncFunc = function() {
    return new Promise((resolve,reject) => {
       setTimeout(_ => resolve('This is async'), 1000)
    })
}

for(let i = 0; i < 5; i++) {
   console.log('This is sync')
   asyncFunc().then(val => console.log(val))
}
for(let i = 0; i < 5; i++) {
   console.log('This is sync')
   console.log(await asyncFunc())
}


如预期的那样,第一个循环输出“ This is sync” 5次,然后输出“ This is async” 5次。
第二个循环输出“ This is sync”,“ This is async”,“ This is sync”,“ This is async”等。

有人可以解释一下两者之间的区别是什么吗,换句话说,异步/等待在幕后究竟做了什么?

最佳答案

您的前两个示例基本上是等效的。但是,你猜怎么着,在这两个示例中不能使用for循环。在await循环中没有一种简单的代码来模仿for语句。这是因为for循环(或while循环)与async/await的交互比插入两个示例中的简单.then()语句要先进。

await暂停执行整个包含函数。这将暂停forwhile循环。要只用.then()进行类似的编程,就必须发明自己的循环结构,因为不能仅用for循环来完成。

例如,如果使用await

let asyncFunc = function() {
    return new Promise((resolve,reject) => {
       setTimeout(_ => resolve('This is async'), 1000)
    })
}

async function someFunc() {
    for(let i = 0; i < 5; i++) {
       console.log('This is sync')
       console.log(await asyncFunc())
    }
}


而且,如果您只想使用.then()作类比,则不能使用for循环,因为没有await就无法“暂停”它。相反,您必须设计自己的循环,通常涉及调用本地函数并维护自己的计数器:

function someFunc() {
    let i = 0;

    function run() {
        if (i++ < 5) {
            console.log('This is sync')
            asyncFunc().then(result => {
                console.log(result);
                run();
            });
        }
    }
    run();
}


或者,有些使用这样的.reduce()构造(尤其是在迭代数组时):

// sequence async calls iterating an array
function someFunc() {
    let data = [1,2,3,4,5];
    return data.reduce((p, val) => {
        console.log('This is sync')
        return p.then(() => {
           return asyncFunc().then(result => {
                console.log(result);
            });
        });
    }, Promise.resolve());
}



  有人可以解释一下两者之间的区别是什么吗,换句话说,异步/等待在幕后究竟做了什么?


在后台,Javascript解释器在await语句中暂停函数的进一步执行。保存当前函数上下文(局部变量状态,执行点等),从函数返回承诺,并继续执行该函数,直到稍后某个待解决的承诺被解决或拒绝为止。如果解析,则将选择相同的执行状态,并且该函数将继续执行更多操作,直到下一个await,依此类推,直到最终该函数不再有await语句且没有更多要执行(或执行到return语句)。

为了进一步说明,这是异步函数的一些背景知识:

声明async函数时,解释器将创建一种特殊的函数,该函数始终返回promise。如果函数显式返回拒绝的promise或函数中存在异常(解释器捕获该异常并将其更改为函数返回的promise的拒绝),则该promise将被拒绝。

如果/当函数返回正常值或仅通过完成执行正常返回时,将解决promise。

当您调用该函数时,它开始像任何普通函数一样同步执行。这包括函数中可能存在的任何循环(如示例中的for循环)。一旦函数遇到第一个await语句,并且其等待的值是一个Promise,则函数将在该点返回并返回其Promise。该功能的进一步执行被暂停。由于该函数返回其诺言,因此该函数调用之后的所有代码将继续运行。稍后的某个时间,当async函数内部等待的诺言得以解决时,会将一个事件插入事件循环以恢复该函数的执行。当解释器返回到事件循环并到达该事件时,将继续执行该函数,而该函数先前在await语句之后的行中停止了。

如果还有其他await语句正在等待promise,则它们将类似地导致函数执行被挂起,直到正在解决或拒绝了正在等待的promise。

这种在中途暂停或暂停执行功能的能力的优点在于,它可以在诸如forwhile之类的循环结构中工作,并且使得使用这种循环依次执行异步操作更容易编程在我们拥有asyncawait之前(您已经发现)。仅使用.then()并没有简单的类似方法来编码测序循环。如上所示,有些使用.reduce()。其他人也使用内部函数调用,如上所示。

关于javascript - Javascript异步/等待行为说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51854535/

相关文章:

javascript - Jade /Expressjs : Pass objects from server to client

c# - 如何在 C# 中创建和使用自定义 Awaitable?

javascript - Promise 链拆分以执行多个异步任务

javascript - "return await"有什么区别吗?

javascript - react : Conditionally render a component in a route based on login status, 而不显示加载页面

javascript - 表单验证不验证密码

javascript - 即使某些项目为空,如何检查数组中的所有项目?

c# - 在异步委托(delegate)中断言异常

c# - 如何从异步方法向调用者抛出异常?

javascript - JS ES6 promise 链接