我想知道,如果等待已解决的诺言,将导致同步代码执行或可能导致异步代码执行。
我做了这个小片段来检查浏览器:
const promise = new Promise((resolve) => {
console.log('exec promise');
setTimeout(() => {
console.log('executed promise');
resolve();
}, 1000);
});
(async () => {
console.log('start');
for (let i = 0; i < 1e8; i += 1) {
await promise;
}
console.log('end');
})();
看起来浏览器使其同步(考虑屏幕冻结)。
但是...是由于浏览器特定的实现吗?还是设计使然?
答案 0 :(得分:1)
这是一个可选的演示,它不会冻结浏览器(我认为这正像我们试图展示的那样),并且显示了与您最初结论相反的行为:
const p = new Promise((r) => r());
p.then(() => {
(async() => {
console.log('a');
await p;
console.log('p');
})()
console.log('b');
});
在这种情况下,我们拥有p
(已解决的承诺)。然后,我们启动等待它的异步函数。输出为:
a
b
p
IE异步功能击中已解决的b
上的await
时,仍然会返回控制(并记录p
)。仅在记录b
后,事件循环才可以自由返回执行,随后返回await
后再记录。
这是标准行为吗?是的。
specification有效地将等待的表达式转换为承诺的p
部分,并且subsequent step的第9步和第10步决定如何进行。
- 如果promise。[[PromiseState]]待处理,则
一种。将promiseReaction追加为promise的List的最后一个元素。[[PromiseFulfillReactions]]。
b。追加rejectReaction作为诺言的List的最后一个元素。[PromiseRejectReactions]]。- 否则,如果实现了诺言。[[PromiseState]],
一种。然后让价值成为承诺。[[PromiseResult]]。
b。设fulfillJob为NewPromiseReactionJob(fulfillReaction,value)
C。执行HostEnqueuePromiseJob(fulfillJob。[[Job]],performJob。[[Realm]])。
第9步说,如果未完成,则将生成的then
添加到实现诺言时要调用的事物列表中。
步骤10成为您问题的核心-它说是否已经完成,请排队工作-即将其放置在待调用的事物队列中。
该规范有效地表明then
永远不应同步返回。
答案 1 :(得分:1)
这是预期的行为。您将事件循环冻结在那里,因为Promise已经解决,所以下一个await运算符将在事件循环的同一迭代中等待并解析下一个Promise描述符。但是,此行为可能是特定于平台的,具体取决于您实际使用的promise实现是本机还是shim。
const promise= Promise.resolve();
(async()=>{
for(let i=0; i< 10000000; i++) {
await promise;
}
console.log('end');
})()
setTimeout(()=> console.log('nextEventLoopTick'), 0);
将输出以下内容:
end
nextEventLoopTick
如您所见,异步fn将在setTimeout之前得到完全解析。因此,这表明异步功能是在相同的事件循环刻度上解决的。