等待单一承诺与异步迭代器的异步解析顺序

Async resolution order for await single promise vs async iterators

所以这是基于我在开发一个简化异步生成器或迭代器的包时的发现。

通常 promise 的执行顺序由它被调用的时间决定,这意味着以下内容是正确的(在 Chrome & Node on Windows & Mac)。

let resolve, promise = new Promise(r => resolve = r);

(async () => {
  await promise
  console.log('1st')
})();
(async () => {
  await promise
  console.log('2nd')
})();

resolve();

但是,在处理异步生成器或迭代器时不遵守此顺序

let resolve, promise = new Promise(r => resolve = r);

async function* generator() {
   yield promise
}

(async () => { // regular promise
  await promise
  console.log('1st')
})();
(async () => { // promise generator
 for await (let _ of generator())
   console.log('3rd (should be 2nd)')
})();
(async () => { // promise iterator
 for await (let _ of [promise])
   console.log('4th (should be 3rd)')
})();
(async () => { // regular promise again
  await promise
  console.log('2nd (should be 4th)')
})();

resolve();

我不确定 "execution /resolution order" 是否是正确的术语,但是这个顺序有保证吗?有什么办法可以在节点或浏览器程序中保证这个顺序吗?

保证解析顺序:

25.6.1.8 TriggerPromiseReactions ( reactions, argument )

[...]

  1. For each reaction in reactions, in original insertion order, do

    a. Perform EnqueueJob("PromiseJobs", PromiseReactionJob, << reaction, arguments >>)

要了解迭代器的运行情况,请查看以下代码片段:

let resolve, promise = new Promise(r => resolve = r);

let chained = promise.then(it => Promise.resolve());

(async () => {
  await chained;
  console.log("2nd");
})();

(async () => { // regular promise
  await promise
  console.log('1st')
})();


resolve();

如您所见,一个 Promise 在另一个 Promise 解决时得到解决将需要两个微滴。

这就是异步迭代器中发生的事情。当您调用 .next 时,将返回一个 Promise 并将其存储在迭代器内部队列中。与此同时,异步生成器函数的执行还在继续。然后,当异步迭代器 yields (在您的情况下,在一个微滴答之后),它解决了队列中的下一个承诺。由于 promise 解决是另一个微任务,因此总共需要两个 ticks。

resolve();
// 1 microtick
await promise; // generators await yielded promises implicitly
yield; 
// 1 microtick
for await(const _ of iterator)