我一直以为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
暂停执行整个包含函数。这将暂停for
和while
循环。要只用.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。这种在中途暂停或暂停执行功能的能力的优点在于,它可以在诸如
for
和while
之类的循环结构中工作,并且使得使用这种循环依次执行异步操作更容易编程在我们拥有async
和await
之前(您已经发现)。仅使用.then()
并没有简单的类似方法来编码测序循环。如上所示,有些使用.reduce()
。其他人也使用内部函数调用,如上所示。
关于javascript - Javascript异步/等待行为说明,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51854535/