异步堆栈追踪:为什么 await 胜过 Promise?

更新日期: 2020-03-12阅读: 1.6k标签: 堆栈

与直接使用 Promise 相比,使用 async/await 不仅可以使代码更具可读性,而且还可以在 JavaScript 引擎中实现一些有趣的优化。

这篇文章是关于一个这样的优化,涉及异步代码的堆栈追踪。

async/await 和 Promise 的根本区别在于 await fn() 暂停当前函数的执行,而 promise.then(fn) 在将 fn 调用添加到回调链后,继续执行当前函数。

const fn = () => console.log('hello')
const a = async () => {
  await fn() // 暂停 fn 的执行
}
// 调用 a 时,才恢复 fn 的执行
a() // "hello"

const promise = Promise.resolve()
// 将 fn 添加到回调链后,继续执行 fn
promise.then(fn) // "hello"

在堆栈追踪的上下文中,这种差异非常显著。

当一个 Promise 链(无论是否脱糖化)在任何时候抛出一个未经处理的异常时, JavaScript 引擎都会显示一条错误信息和(希望)记录一个有用的堆栈追踪。

作为一名开发人员,无论您使用的是普通的 Promise 还是 async await,您都会期望这样。


Promise

想象一个场景,当对异步函数 b 的调用解析时,调用函数 c:

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

当调用 a 时,将同步发生以下情况:

  • b 被调用并返回一个 Promise,该 Promise 将在将来某个时刻解决。
  • .then 回调(实际上是调用 c())被添加到回调链中( V8 术语中,[…]被添加为解析处理程序)。

之后,我们完成了在函数 a 的主体中执行代码。a 永远不会被挂起,当对 b 的异步调用解析时,上下文已经消失了。

想象一下如果 b(或 c )异步抛出异常会发生什么?理想情况下,堆栈追踪应该包括 a,因为 b(或 c)是从那里调用的,对吧?既然我们不在参考 a 了 ,那怎样能做到呢?

为了让它工作,JavaScript 引擎需要在上面的步骤之外做一些事情:它在有机会的时候捕获并存储堆栈追踪。

在 V8 中,堆栈追踪附加到 b 返回的 Promise。当 Promise 实现时,堆栈追踪将被传递,以便 c 可以根据需要使用它。

b()[a] -> b().then()[a] -> c[a?:a]

捕获堆栈追踪需要时间(即降低性能);存储这些堆栈追踪需要内存。


async/await

下面是同样的程序,使用 async/await 而不是 Promise 编写:

const b = () => Promise.resolve()
const a = async () => {
  await b()
  c()
}

使用 await,即使在 await 调用中不收集堆栈追踪,我们也可以恢复调用链。

这是可能的,因为 a 被挂起,正在等待 b 解决。如果 b 抛出异常,则可以按需以这种方式重建堆栈追踪。

如果 c 抛出异常,堆栈追踪可以像同步函数那样构造,因为发生这种情况时,我们仍在 a 上下文中。

通过遵循以下建议,使 JavaScript 引擎能够以更高效的方式处理堆栈追踪:

  • 偏好 async/await 胜过 Promise。
  • 使用 @babel/preset env 避免不必要的 async/await 传输。

原文:https://segmentfault.com/a/1190000022610658

链接: https://www.fly63.com/article/detial/8951

如何优雅地查看 JS 错误堆栈?

在前端,我们经常会通过 window.onerror 事件来捕获未处理的异常。假设捕获了一个异常,上报的堆栈是这个:这个堆栈,你看得出问题来吗?我们发布到 CDN 的脚本文件,普遍是经过 UglifyJS 压缩的,所以堆栈可读性相当的差。

连续赋值(从堆栈角度解析) a.x = a = {n:2}

今天看到一个面试题,一直想把这个题目解析更加直观化,就跟看小人书一样,看图就能明白其中的原理,所以用PPT做了几张图。接下来我们从以下几点分析以下:

JavaScript中的执行上下文和堆栈是什么?

在这篇文章中,我将深入研究JavaScript最基本的部分之一,即执行上下文。在这篇文章的最后,您应该更清楚地了解解释器要做什么,为什么在声明一些函数/变量之前可以使用它们,以及它们的值是如何确定的。

js中的堆和栈

栈(stack):栈会自动分配内存空间,会自动释放,存放基本类型,简单的数据段,占据固定大小的空间;堆(heap):动态分配的内存,大小不定也不会自动释放

5 张图描绘Web3 堆栈全景

Web3 堆栈最令人难以置信的一点是,它们不需要任何集中协调就可以组合在一起。开发本身是去中心化的。没有主架构师。这与地球上几乎所有其他的开发堆栈项目形成了鲜明的对比。在 Linux 基金会,少数人设定整个 Linux 的方向

使用 JavaScript 的数据结构:堆栈和队列

Web 开发中最常用的两种数据结构是堆栈和队列。许多 Internet 用户,包括 Web 开发人员,都没有意识到这一惊人的事实。如果您是这些开发人员中的一员,那么请准备好两个具有启发性的示例:文本编辑器的撤消操作使用堆栈来组织数据

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!