Javascript:等待已解决的诺言

时间:2020-10-15 08:44:04

标签: javascript promise async-await

我想知道,如果等待已解决的诺言,将导致同步代码执行或可能导致异步代码执行。

我做了这个小片段来检查浏览器:

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');
})();

看起来浏览器使其同步(考虑屏幕冻结)。

但是...是由于浏览器特定的实现吗?还是设计使然?

2 个答案:

答案 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步决定如何进行。

  1. 如果promise。[[PromiseState]]待处理,则
    一种。将promiseReaction追加为promise的List的最后一个元素。[[PromiseFulfillReactions]]。
    b。追加rejectReaction作为诺言的List的最后一个元素。[PromiseRejectReactions]]。
  2. 否则,如果实现了诺言。[[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之前得到完全解析。因此,这表明异步功能是在相同的事件循环刻度上解决的。