为什么这个异步函数会在它之前定义的等价 Promise.then 链之前执行?

Gra*_*ant 17 javascript promise async-await es6-promise

我有以下代码:

var incr = num => new Promise(resolve => {
  resolve(num + 1);
});

var x = incr(3)
  .then(resp => incr(resp))
  .then(resp => console.log(resp));

async function incrTwice(num) {
  const first = await incr(num);
  const twice = await incr(first);
  console.log(twice);
}

incrTwice(6);
Run Code Online (Sandbox Code Playgroud)

我相信(可能是错误的)显示了实现相同功能的两种等效方法:首先通过链接承诺,其次使用 async/await 的语法糖。

我希望promise 链接解决方案首先是console.log,然后是异步函数,但是异步函数console.log 的第一个然后是promise 链接解决方案打印。

我的逻辑如下:

  1. xs 初始解析将在处理声明时首先出现在微任务队列中
  2. x和的声明之间堆栈是空的,incrTwice这将导致微任务队列被刷新(导致承诺链的完成)
    • x 先打印
  3. incrTwice 被定义为
  4. incrTwiceawaits处的微任务队列上执行排队,最终打印到控制台
    • incrTwice 打印第二个

显然我在某处有误解,有人能够指出我错在哪里吗?

Ber*_*rgi 13

首先,我要指出,你永远不应该争论独立承诺链的执行顺序。有两个异步调用,它们不相互依赖而是并发运行,因此应该始终期望它们以任意顺序完成。

仅使用立即解决承诺的玩具示例使此顺序依赖于微任务队列语义而不是实际的异步任务,这使得这成为纯粹的学术练习(其结果可能会在规范中发生变化)。

无论如何,让我们澄清你的误解:

x和的声明之间堆栈是空的,incrTwice这将导致微任务队列被刷新

不,堆栈仅在所有用户代码运行完成后才变为空。<script>堆栈上仍然有元素的全局执行上下文。在所有同步代码(incr = …x = incr(3).…incrTwice(6))完成之前,不会执行任何微任务。

我相信 [代码] 展示了实现相同功能的两种等效方法:首先通过链接承诺,其次使用 async/await 的语法糖。

不完全是。.then()在取消incr(resp)从第一个.then(…)处理程序返回的承诺时,链接有一个额外的解析步骤。要使其行为与awaited 中的承诺完全相同incrTwice,您需要编写

incr(3).then(resp =>
  incr(resp).then(resp =>
    console.log(resp)
  )
);
Run Code Online (Sandbox Code Playgroud)

如果这样做,您实际上console将按照启动两个承诺链的顺序获得日志,因为在console.log()执行之前它们将执行相同数量的微任务。

有关详细信息,请参见什么是执行JavaScript中承诺的订单承诺中承诺的解决顺序发生什么事,当我们返回值,当我们从一则()链返回Promise.resolve,在microtask队列?返回的Promise有什么区别?, ES6 promise 返回值的执行顺序