【问题标题】:Async resolution order for await single promise vs async iteratorsawait 单个 Promise 与异步迭代器的异步解析顺序
【发布时间】:2020-04-03 10:40:30
【问题描述】:

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

通常情况下,promise 的执行顺序是由它被调用的时间决定的,这意味着以下是正确的(在 Windows 和 Mac 上的 Chrome 和 Node 中)。

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

我不确定“执行/解决顺序”是否是正确的术语,但这个顺序是否得到保证?有什么办法可以在 Node 或 Browser 程序中保证这个顺序?

【问题讨论】:

  • 你确定他们是发电机吗?在我看来,它们不像发电机?这些不是异步迭代器吗?
  • 我宁愿将第一个示例的“固定顺序”视为实现细节。我不是 100% 确定,但我认为规范不能保证任何类型。
  • @InchHigh 你说得对,更新了问题以反映变化。
  • @Amadan 这就是我的怀疑,但一直找不到任何来源(主要是因为我不确定它的实际名称)并且也无法产生不同的结果。
  • @Amadan spec 确实提到应该保留执行顺序。

标签: javascript typescript asynchronous async-await es6-promise


【解决方案1】:

承诺解决顺序有保证:

25.6.1.8 TriggerPromiseReactions(反应,参数)

[...]

  1. 对于reactions中的每个reaction,在原始插入顺序中,做

    一个。执行 EnqueueJob("PromiseJobs", PromiseReactionJob, >)

要了解迭代器发生了什么,请查看以下 sn-p:

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 解析是另一个微任务,因此总共需要两个滴答声。

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

【讨论】:

  • 太棒了,这反映了this spec,这是我所期望的。但是,如果是这种情况,那么为什么第二个示例不尊重插入顺序?
  • @MoPro 是的,开始写答案时,它比我想象的要复杂......
  • @MoPro 是。这里只涉及两个 Promise。
  • 有趣的是,我认为唯一需要 microticks 的是异步函数。让生成器和 yield 取一个微滴是有道理的,但拥有一个简单的迭代器似乎违反直觉。谢谢你的信息!
猜你喜欢
  • 2014-02-04
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
  • 2013-01-31
  • 2018-12-08
  • 1970-01-01
  • 2020-09-13
  • 1970-01-01
相关资源
最近更新 更多