await 的背后
文章目录
我们一般会认为 async/await
是 Promise
的语法糖,实际上也没有错。但在 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
,即 promise
和 throwaway
。同时一个 await
会多产生三个微任务,即执行
promise
时的微任务,由 promise
衍生的,用来决定返回值传播的微任务,即 PromiseReactionJob
,最后就是下一步执行的函数即 then()
或 catch()
。
显然,这里存在这许多优化空间。
首先,对于已经是 Promise
的 v
不必再进行一次封装,这是第一步。
当我们对 Promise
进行 await
时,只会增加一个 Promise
,和一个 throwaway
。
但 throwaway
我们完全不需要,只是 ES 强塞进来的。后面,
ES 更改了这以规范,使得 throwaway
不再是必须的。
最终我们得到了现在的优化。
关于 Bluebird
以前还有关于 Bluebird 还有 V8 哪个更优的争论,但现在, Bluebird
官方更推荐在最新版的 Node 里使用原生 Promise
。
Promise
经过几次优化,并历经几年考验之后,完全不输于
Bluebird 。除非你有特殊情况,比如老版本的 Node ,等等。
文章作者 bigshans
上次更新 2022-12-15