专栏首页Fundebug从JS引擎理解Await b()与Promise.then(b)的堆栈处理

从JS引擎理解Await b()与Promise.then(b)的堆栈处理

译者按: Async/Await 真的只是简单的语法糖吗?No!

本文采用意译,版权归原作者所有

与直接使用 Promise 相比,使用Async/Await不仅可以提高代码的可读性,同时也可以优化 JavaScript 引擎的执行方式。这篇博客将介绍 Async/Await 是如何优化 JavaScript 引擎对堆栈信息的处理。

Async/Await 与 Promise 最大区别在于:await b()会暂停所在的 async 函数的执行;而 Promise.then(b)将 b 函数加入回调链中之后,会继续执行当前函数。对于堆栈来说,这个不同点非常关键。

当一个 Promise 链抛出一个未处理的错误时,无论我们使用 await b()还是 Promise.then(b),JavaScript 引擎都需要打印错误信息及其堆栈。对于 JavaScript 引擎来说,两者获取堆栈的方式是不同的。

Promise.then(b)

示例代码中,函数 c()会在异步函数 b()成功 resolve 之后执行:

const a = () => {
    b().then(() => c());
};

当调用 a()函数时,这些事情同步发生:

  • b()函数被调用,它会返回一个 Promise,这个 Promise 会在未来的某个时刻 resolve。
  • .then()回调函数(实际调用了 c()函数)被添加到回调链。

这样,a()函数内的代码就执行完了。a()函数不会被暂停,因此在异步函数 b()resolve 时,a()函数的作用域已经不存在了。假设 b()或者 c()抛出了一个错误,则堆栈信息中应该包含 a()函数,因为它们都是在 a()函数内被调用。对 a()函数的任何引用都不存在了,要如何生成包含 a()的堆栈信息呢?

为了解决这个问题,JavaScript 引擎需要做一些额外的工作:它会及时记录并且保存堆栈信息。对于 V8 引擎来说,堆栈信息附加在了 b()函数所返回的 Promise 并在 Promise 链中传递,这样 c()函数也能在需要的时候获取堆栈信息。

记录堆栈信息需要时间,这样会降低性能;而保存堆栈信息需要占用额外的内存。

使用Fundebug, 可以实时监控线上应用的错误,并获取完整的堆栈信息。

Await b()

我们可以使用 Async/Await 实现同样的代码,同步函数 c()会等到异步函数 b()执行结束之后再执行:

const a = async () => {
    await b();
    c();
};

使用 await 时,无需存储当前的堆栈信息,因为存储 b()到 a()的指针就足够了。当等待 b()函数执行时,a()函数被暂停了,因此 a()函数的作用域还在内存可以访问。如果 b()函数抛出一个错误,堆栈信息可以通过指针迅速生成。如果 c()函数抛出一个错误,堆栈信息也可以像同步函数一样生成,因为 c()函数是在 a()函数中执行的。不论是 b()还是 c(),我们都不需要去存储堆栈信息,因为堆栈信息可以在需要的时候立即生成。而存储指针,显然比存储堆栈更加节省内存。

建议

很多 ECMAScript 语法特性看起来都只是语法糖,其实并非如此,至少 Async/Await 绝不仅仅只是语法糖。

为了让 JavaScript 引擎处理堆栈的方式性能更高并且更加节省内存,请遵循这些建议:

  • 使用 Async/Await,而不是直接使用 Promise
  • 使用babel-preset-env,避免 Babel 不必要地转换 Async/Await

尽管 V8 引擎还没有实现这些优化,请遵循这些建议。当我们为 V8 实现这些优化的时候,你的程序可以保证最佳的性能。(作者是 V8 引擎的开发者)

一般来说,尽量不要去使用 Babel 转码器。所有支持 Service Workers 的浏览器都支持 Async/Await,因此没有必要去对 Async/Await 转码。这一点对于JavaScript modules via script tag同样适用。关于这一点,大家可以参考Deploying ES2015+ Code in Production Today

版权声明

转载时请注明作者 Fundebug以及本文地址: https://blog.fundebug.com/2018/07/18/javascript-engine-await-promise/

您的用户遇到BUG了吗?

体验Demo 免费使用

.copyright *{box-sizing:border-box}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JavaScript深入浅出第2课:函数是一等公民是什么意思呢?

    看到一篇讲JavaScript历史的文章里面提到:JavaScript借鉴Scheme语言,将函数提升到"一等公民"(first class citizen)的...

    Fundebug
  • 一文带你了解JavaScript函数式编程

    函数式编程在前端已经成为了一个非常热门的话题。在最近几年里,我们看到非常多的应用程序代码库里大量使用着函数式编程思想。

    Fundebug
  • 重构:从Promise到Async/Await

    摘要: 夸张点说,技术的发展与历史一样,顺之者昌,逆之者亡。JS开发者们,赶紧拥抱Async/Await吧!

    Fundebug
  • 函数讲解

    罗罗攀
  • Python+sklearn机器学习应该了解的33个基本概念

    机器学习(Machine Learning)根据已知数据来不断学习和积累经验,然后总结出规律并尝试预测未知数据的属性,是一门综合性非常强的多领域交叉学科,涉及线...

    Python小屋屋主
  • Python入门 (二)

    本文是个人python学习笔记,学习资料为廖雪峰python教程,如需更多内容,请移步廖老师官方网站。

    py3study
  • 用一个属性代理另一个对象的属性

    今天来分享给大家一个属性代理的例子。总是有人问我属性代理有什么用,这个也许可以为你提供些思路。

    bennyhuo
  • GMIS 2017大会邓力主题演讲:无监督学习的最新进展

    机器之心原创 记者:蒋思源 全球机器智能峰会(GMIS 2017),是全球人工智能产业信息服务平台机器之心举办的首届大会,邀请了来自美国、欧洲、加拿大及国内的众...

    机器之心
  • 关于python的bottle框架跨域请求报错问题的处理

      在用python的bottle框架开发时,前端使用ajax跨域访问时,js代码老是进入不了success,而是进入了error,而返回的状态却是200。ur...

    用户1214487
  • python技能(1)-map函数

    yiduwangkai

扫码关注云+社区

领取腾讯云代金券