花粉乐分享平台宣传视频
> 华为资讯 > 华为资讯 > 鸿蒙编程 > 如何在V8中优化JavaScript异步编程?
如何在V8中优化JavaScript异步编程?
来源:wulili1100
2023-02-22 16:14:03
607
管理

执行在调用者中继续,直到调用栈变空。然后 JavaScript 引擎开始运行微任务:它运行先前调度的 PromiseResolveThenableJob,它会调度新的 PromiseReactionJob,并将 promise 链接到传给 await 的值。然后,引擎返回去处理微任务队列,因为在继续主事件循环之前必须清空微任务队列。

对于每个 await,引擎都必须创建两个额外的 promise,并需要至少三个微任务。谁会想到一个 await 表达式会导致这么多的开销?!

我们来看看这些开销来自哪里。第一行负责创建包装器 promise,第二行立即用 await 值 v 完成了包装器 promise。这两行负责一个额外的 promise 加上三个微任务中的两个。如果 v 是一个 promise(这是很常见的情况,因为应用程序通常会 await promise),开销就非常大。比如,开发人员在 42 上 await,那么引擎仍然需要将它包装成一个 promise。

事实证明,规范中已经有一个 promiseResolve 操作,它只在必要时执行包装:

将 Node.js 10 中的 await 与可能在 Node.js 12 中发布的优化版 await 进行比较,显示了这个变更对性能的影响:

async/await 现在优于手写的 promise 代码。这里的关键点是我们通过修改规范显著减少了异步函数的开销——不仅在 V8 中,而且是在所有的 JavaScript 引擎中。

改进的开发者体验

除了性能之外,JavaScript 开发者还关心诊断和修复 bug 方面的问题,这些在处理异步代码时并不总是那么容易。Chrome DevTools 支持异步堆栈跟踪,堆栈跟踪不仅包括堆栈的当前同步部分,还包括异步部分:

在进行本地开发时,这是非常有用的一项功能,但对于已经部署的应用程序,这种方法并不会真正帮助到你。在进行事后调试时,你只会在日志文件中看到 Error#stack 输出,它们并不会告诉你有关异步部分的任何信息。

我们最近一直在研究零成本的异步堆栈跟踪,通过异步函数调用丰富了 Error#stack 属性。”零成本”听起来很令人兴奋,不是吗?Chrome DevTools 会带来重大的开销,那它又是如何做到零成本的?可以看看这个示例,其中 foo 异步调用 bar,bar 在 await promise 时抛出异常:

async function foo() { await bar(); return 42;}async function bar() { await Promise.resolve(); throw new Error('BEEP BEEP');}foo().catch(error => console.log(error.stack));

在 Node.js 8 或 Node.js 10 中运行这段代码将产生以下输出:

$ node index.jsError: BEEP BEEP at bar (index.js:8:9) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:745:11) at startup (internal/bootstrap/node.js:266:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:595:3)

请注意,虽然对 foo() 的调用会导致错误,但 foo 不是堆栈跟踪的一部分。这让 JavaScript 开发人员在进行事后调试时感到很为难,无论你的代码是部署在 Web 应用程序中还是部署在云容器内。

有趣的是,当 bar 完成时,引擎知道该从哪里继续:在函数 foo 的 await 之后。巧合的是,这也是函数 foo 被暂停的位置。引擎可以使用这些信息来重建部分异步堆栈跟踪,于是输出变为:

$ node --async-stack-traces index.jsError: BEEP BEEP at bar (index.js:8:9) at process._tickCallback (internal/process/next_tick.js:68:7) at Function.Module.runMain (internal/modules/cjs/loader.js:745:11) at startup (internal/bootstrap/node.js:266:19) at bootstrapNodeJSCore (internal/bootstrap/node.js:595:3) at async foo (index.js:2:3)

在堆栈跟踪中,最顶层的函数首先出现,然后是同步堆栈跟踪的其余部分,再然后是函数 foo 中对 bar 的异步调用。在 V8 中,这个变更是通过 --async-stack-traces 标志实现的。

但是,如果将它与 Chrome DevTools 中的异步堆栈跟踪进行比较,你会注意到,堆栈跟踪的异步部分中缺少 foo 的实际调用信息。如前所述,这种方法利用了以下事实:对于 await 来说,恢复和暂停位置是相同的——但对于常规的 Promise#then() 或 Promise#catch() 调用,情况并非如此。

结 论

我们进行了两个重要的优化,让异步函数变得更快:

移除两个额外的 microtick;移除 throwaway promise。

最重要的是,我们通过零成本的异步堆栈跟踪改进了开发者体验。

我们还为 JavaScript 开发人员提供了一些很好的性能改进建议:

使用异步函数和 await 代替手写的 promise 代码;坚持使用 JavaScript 引擎提供的原生 promise 实现,这样可以避免为 await 使用两个 microtick。

英文原文

https://v8.dev/blog/fast-async

花粉社群VIP加油站

2
点赞
赏礼
赏钱
0
收藏
免责声明:本文仅代表作者个人观点,与花粉乐分享无关。其原创性以及文中陈述文字和内容未经本网证实,对本文以及其中全部或者 部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
凡本网注明 “来源:XXX(非花粉乐分享)”的作品,均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对 其真实性负责。
如因作品内容、版权和其它问题需要同本网联系的,请在一周内进行,以便我们及时处理。
QQ:2443165046 邮箱:info@hflfx.com
关于作者
放开那只猫..(采蜜高手)
文章
548
主题
0
关注
0
粉丝
0
点击领取今天的签到奖励!
签到排行
随手拍
54个圈友 0个话题
华为手机随手拍,记录生活点滴之美好
华为P30pro
51个圈友 0个话题
这里是华为P30pro手机交流圈,欢迎华为P30pro用户进群交流
体验官
60个圈友 2个话题
华为花粉体验官,体验官专属的交流群
登录后查看您创建的圈子
登录后查看您创建的圈子
所有圈子
杭州互联网违法和不良信息举报平台 网络110报警服务 浙ICP备17046585号
2
0
分享
请选择要切换的马甲:

个人中心

每日签到

我的消息

内容搜索