当 Promise 而不是数字时, await 行为是否应该有所不同?

Should await behavior be different when it is followed from a Promise rathen than a number?

我正在学习 await/async 关键字。 我的书说以下代码输出从 1 到 9 而不是 Firefox 和 Chrome 输出 1 2 3 4 5 8 9 6 7 我同意浏览器。 当 await 跟随 Promise 时,我的书强调了不同的行为。 也就是说,await Promise.resolve(1)await 1 的处理程序在队列中的放置方式不同,并且它说明了不同的队列。这是书的错误吗? 在我的理解中,当遇到 await 66 被转换为 Promise(6),然后 Promise(6).then(handler) 在幕后发生,同步模式执行被中断,Promise 已经解决了处理程序被放入作业队列。控制传递给继续 运行 剩余同步外部代码的调用函数。稍后执行作业队列中的处理程序,并以异步方式执行 bar function 中的剩余代码。 await 6 将 return 6.

当我执行 Promise.resolve(8) 时,同样的事情应该发生并且 8 将在 6 之前打印,因为 foo function 首先被调用,因此处理程序被放在作业队列的第一位。

我错过了什么吗?这是书本错误还是同时发生了一些变化,或者可能是这种特定行为是浏览器依赖的,因为它没有在 ecmascript 规范中指定? 此外,我想知道是否在事件 Loop/Message Queue/Job 引入 async/await ()

队列机制中更改了某些内容

async function foo(){
  console.log(2);
  console.log(await Promise.resolve(8));
  console.log(9);
}

async function bar(){
   console.log(4);
   console.log(await 6);
   console.log(7);
}

console.log(1);
foo();
console.log(3);
bar();
console.log(5);

这里是本书的页面,里面有相关的代码和解释。 Page1

Page2

是的,await 6await Promise.resolve(6),如 here 所述。

因此您的代码变为:

async function foo(){
  console.log(2);
  console.log(await Promise.resolve(8));
  console.log(9);
}

async function bar(){
   console.log(4);
   console.log(await Promise.resolve(6));
   console.log(7);
}

console.log(1);
foo();
console.log(3);
bar();
console.log(5);

没有理由第二个等待发生在第一个等待之前。自然8会先于6解决。因此,是的,这本书是错的。

但是,如果 8 的承诺做了一些实际的等待,那将是正确的。例如:

async function foo(){
  console.log(2);
  console.log(await new Promise(resolve => setTimeout(() => resolve(8), 100)));
  console.log(9);
}

async function bar(){
   console.log(4);
   console.log(await Promise.resolve(6));
   console.log(7);
}

console.log(1);
foo();
console.log(3);
bar();
console.log(5);

这确实打印了书中所说的输出:

1
2
3
4
5
6
7
8
9

作者假设一个比另一个需要更长的时间,或者 he/she 碰巧使用了一个奇怪的 js 引擎,谁知道呢。

本书作者在这里!

编写本章时,我正在 Firefox v64(及其当前的供应商版本)上进行测试。如果你安装这个 version of the browser,你会发现这个例子可以正常工作。

我将中断版本缩小到 Firefox 69,它是在编辑团队编写和测试本章后几个月发布的。您会注意到在 release notes 部分的 "APIs" 部分下有一个项目符号 "The Microtask API (WindowOrWorkerGlobalScope.queueMicrotask()) has been implemented".

relevant section from the HTML specification内容如下:

...the best way of thinking about queueMicrotask() is as a mechanism for rearranging synchronous code, effectively placing the queued code immediately after the current task's worth of non-queued JavaScript.

现代浏览器中的 promise 实现使用了微任务 API,它改变了这个例子的执行顺序。具体来说,书中的例子使用了 await Promise.resolve(8) 将有效地延迟此日志语句两个周期 - 一次用于等待,一次用于 Promise.resolve()。现在promises使用了microtask queue,就不是这样了,所以书中的日志输出是不正确的。

已报告此错误 earlier this year 并且正在从书中删除。