我一直认为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”等。
有人可以解释一下两者之间的区别吗,换句话说,异步/等待在幕后究竟做了什么?
答案 0 :(得分:2)
您的前两个示例基本上是等效的。但是,你猜怎么着,在这两个示例中不能使用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()
。其他人也使用内部函数调用,如上所示。
答案 1 :(得分:1)
异步/等待是下一代的承诺。基本上,await将保留其余代码的执行,直到await解析为止。 例如
async function getUser() {
const id = 'zzzz';
const user = await get(`http://getuser.xxx/{id}`);
console.log(user);
}
执行get函数后唯一的控制台被打印。
答案 2 :(得分:1)
.Select
会按照包装盒上的说明进行操作:等待异步函数的响应:
await
这将记录let asyncFunc = () =>
new Promise((resolve,reject) => {
setTimeout(() => resolve("This is async"), 5000)
});
console.log("before await");
console.log(await asyncFunc());
console.log("after await");
,然后在5秒钟后将记录"before await"
,然后记录"This is async"
。
它基本上使异步功能同步运行。
答案 3 :(得分:0)
孤立地说,是async/await
是诺言的语法糖。您缺少更大的上下文:承诺本身就在那里,因为JS在await
出现之前就无法暂停函数的执行。为了模拟这种暂停效果,您必须转换整个同步函数(在本例中为for
循环),而不仅仅是await
行。
首先,我们必须将for
循环展开为while
(谈到语法糖!),然后才能将其重写为递归(因为await
之前的版本只能在功能之间“停止”,而不是在它们内部),那么我们可以将其重新布线以仅在实现承诺时递归。这是将您的代码从async/await
转换为promises时的代码:
let asyncFunc = function() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve('This is async'), 1000)
});
};
function loop(i) {
if (i < 5) {
console.log('This is sync');
return asyncFunc().then(result => {
console.log(result);
return loop(++i);
});
}
}
loop(0);
您现在基本上拥有两种功能:async
和非async
功能,并且只有async
个功能中可以包含await
(否则,您将获取“ SyntaxError:等待仅在异步功能中有效”)。这是一种原因:为了将await
语义“重写”为非await
语义,您需要更改整个函数,而不仅仅是await
行。它是一种语法糖,但是在整个功能级别,而不是在OP示例中的语句级别。
编辑:您还可以基本上读出原始代码中需要完成的工作:
for(let i = 0; i < 5; i++) {
console.log('This is sync')
console.log(await asyncFunc())
}
让循环以
i
为0
开始。检查i
是否小于5
;如果是这样,请记录“ This is sync”,并调用asyncFunc
(并等待其完成)。 然后,打印出结果,增加计数器并继续循环。
尽管代码相对于JavaScript语法进行了大幅度的重新排列,但听起来还是很像我写的内容,不是吗? :)
答案 4 :(得分:0)
async/await
可能是语法糖,但不仅仅是then()
周围的糖。 generators + promises。生成器时,您会注意到语法几乎与async/await
相同,但是使用async/await
而不是await。
例如,您可以编写一个看起来与第二个yield
循环几乎完全一样的生成器,它将产生相同的行为:
for