V8中更快的异步函数和promises
await 的开销总结以上所学,对于每个 await,引擎都必须创建两个额外的 promise(即使右边的表达式已经是 promise)并且它需要至少三个 microtask 队列执行。 谁知道一个简单的 await 表达式会引起这么多的开销呢?! 事实证明,规范中已经有 promiseResolve 操作,只在必要时执行封装: 此操作一样会返回 promises,并且只在必要时将其他值包装到 promises 中。 通过这种方式,你可以少用一个额外的 promise,以及 microtask 队列上的两个 tick,因为一般来说传递给 await 的值会是 promise。 这种新行为目前可以使用 V8 的 --harmony-await-optimization 标志实现(从 V8 v7.1 开始)。 我们也向 ECMAScript 规范提交了此变更,该补丁会在我们确认它与 Web 兼容之后马上打上。 以下展示了新改进的 await 是如何一步步工作的: 最终当所有 JavaScript 执行完成时,引擎开始运行 microtask,所以 PromiseReactionJob 被执行。 这个工作将 promise 的结果传播给 throwaway,并恢复 async 函数的执行,从 await 中产生 42。 await overhead假如传递给 await 的值已经是一个 promise,那么这种优化避免了创建 promise 封装器的需要,这时,我们把最少三个的 microticks 减少到了一个。 这种行为类似于 Node.js 8 的做法,不过现在它不再是 bug 了 - 它是一个正在标准化的优化! 尽管引擎完全内置,但它必须在内部创造 throwaway promise 仍然是错误的。 事实证明,throwaway promise 只是为了满足规范中内部 performPromiseThen 操作的 API 约束。 最近的 ECMAScript 规范解决了这个问题。 引擎不再需要创建 await 的 throwaway promise - 大部分情况下[2]。 await code before and after the optimizations将 Node.js 10 中的 await 与可能在 Node.js 12 中得到优化的 await 对比,对性能的影响大致如下: 除了性能之外,JavaScript 开发人员还关心诊断和修复问题的能力,这在处理异步代码时并没那么简单。 Chrome DevTool 支持异步堆栈跟踪,该堆栈跟踪不仅包括当前同步的部分,还包括异步部分: 这在本地开发过程中非常有用。 但是,一旦部署了应用,这种方法就无法起作用了。 在事后调试期间,你只能在日志文件中看到 Error#stack 输出,而看不到任何有关异步部分的信息。 我们最近一直在研究零成本的异步堆栈跟踪,它使用异步函数调用丰富了 Error#stack 属性。 “零成本”听起来很振奋人心是吧? 当 Chrome DevTools 功能带来重大开销时,它如何才能实现零成本? 举个例子🌰,其中 foo 异步调用了 bar ,而 bar 在 await promise 后抛出了异常: 在 Node.js 8 或 Node.js 10 中运行此代码会输出: |
Powered by Discuz! X3.4
© 2001-2023 Discuz! Team.