我们一般会认为 async/awaitPromise 的语法糖,实际上也没有错。但在 Node 10 的时候,一个 await 会产生三个微任务,导致单个 await 的性能远不如 Promise ,但在 Node 12 时, V8 团队称,其性能已经比一般手动创建处理的 Promise 好了。

为什么会这样呢?

我们首先要理解 async/await 是如何实现的。一个函数被标记为 async ,它会被 V8 标记为 resumable 的,意为可暂停的。 V8 首先会创建一个隐式的 Promise ,等到执行到 await 时,将函数的 handler 传递给等待执行的 Promise ,然后挂起函数。

在 Node 10 时,代码执行大概是这样的。

我们可以看到, V8 无论何时都会包装一边 v ,以确保 thenable 。在中间, V8 又创建 throwaway ,这个 Promise 纯粹是为了符合 ES 标准。 V8 把函数的 handler 传入到了新生成的 Promise 的 then 里面。最后挂起整个函数。等到前面的 Promise 完成了执行,就会恢复执行隐式的 Promise

首先,我们至少会多产生两个 Promise 和三个微任务。

为什么呢?一个 await 至少会多产生两个 Promise ,即 promisethrowaway 。同时一个 await 会多产生三个微任务,即执行 promise 时的微任务,由 promise 衍生的,用来决定返回值传播的微任务,即 PromiseReactionJob ,最后就是下一步执行的函数即 then()catch()

显然,这里存在这许多优化空间。

首先,对于已经是 Promisev 不必再进行一次封装,这是第一步。

当我们对 Promise 进行 await 时,只会增加一个 Promise ,和一个 throwaway

throwaway 我们完全不需要,只是 ES 强塞进来的。后面, ES 更改了这以规范,使得 throwaway 不再是必须的。

最终我们得到了现在的优化。

关于 Bluebird

以前还有关于 Bluebird 还有 V8 哪个更优的争论,但现在, Bluebird 官方更推荐在最新版的 Node 里使用原生 PromisePromise 经过几次优化,并历经几年考验之后,完全不输于 Bluebird 。除非你有特殊情况,比如老版本的 Node ,等等。


参考: Faster async functions and promises