异步堆栈跟踪:为什么 await 击败了 Promise#then()
与直接使用 promises 相比,async
和 await
不仅可以使开发人员的代码更具可读性——它们还可以在 JavaScript 引擎中实现一些有趣的优化! 这篇文章是关于这样一种优化,涉及异步代码的堆栈跟踪。
await
和 vanilla promises
的根本区别在于 await X()
暂停当前函数的执行,而 promise.then(X)
在将 X 调用添加到回调链后继续执行当前函数。 在堆栈跟踪的上下文中,这种差异非常显着。
当 promise
链在任何时候抛出未处理的异常时,JavaScript 引擎会显示错误消息和(希望如此)有用的堆栈跟踪。 作为一名开发人员,无论我们使用普通的 promise
还是 async
和 await
,我们都期望这一点。关于 promise 也可以参考我们之前的如何创建自己的 Promise这篇文章。
Vanilla promise
想象一个场景,当对异步函数 b
的调用解析时调用函数 c
:
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 可以根据需要使用它。
捕获堆栈跟踪需要时间(即降低性能); 存储这些堆栈跟踪需要内存。
async/await
这是同一个程序,使用 async/await
而不是 vanilla promises 编写:
const a = async () => {
await b();
c();
};
使用 await
,即使我们没有在 await
调用时收集堆栈跟踪,我们也可以恢复调用链。 这是可能的,因为 a 被挂起,等待 b 解析。 如果 b 抛出异常,则可以通过这种方式按需重建堆栈跟踪。 如果 c 抛出异常,则可以像同步函数一样构建堆栈跟踪,因为我们仍在发生这种情况的时候。
建议
与大多数看似“只是语法糖”的 ECMAScript 功能一样,async/await
不止于此。
遵循以下建议,使 JavaScript 引擎能够以更高效和内存效率更高的方式处理堆栈跟踪:
-
比 vanilla promises 更喜欢
async/await
。 -
使用
@babel/preset-env
避免不必要地转译async/await
。
尽管 V8 尚未实现此优化,但遵循此建议可确保我们(或其他 JavaScript 引擎)实现后的最佳性能。
相关文章
在 TypeScript 中返回一个 Promise
发布时间:2023/03/19 浏览次数:182 分类:TypeScript
-
本教程讨论如何在 TypeScript 中返回正确的 Promise。这将提供 TypeScript 中 Returns Promise 的完整编码示例,并完整演示每个步骤。
在 JavaScript 中等待 Promise 得到解决
发布时间:2023/03/10 浏览次数:257 分类:JavaScript
-
在本文中,我们将研究如何在执行 JavaScript 中的下一部分代码之前等待 Promise 得到解决。
深度理解 ES6 Promise
发布时间:2023/03/07 浏览次数:138 分类:WEB前端
-
现在我们开始 ES6 深度系列。 如果大家以前从未来到过这里,请从 ES6 教程 开始。 这篇是关于 ES6 中的 Promise API。 如果觉得该动画非常复杂,请继续阅读! Promises 是一个非常复杂的范
如何解决 TypeScript 中 Type 'Promise' is not assignable to type 错误
发布时间:2023/01/31 浏览次数:365 分类:TypeScript
-
当我们尝试将具有 Promise 类型的值分配给具有不兼容类型的值时,会发生 Type Promise is not assignable to type 错误。 要解决错误,需要在赋值之前解决 Promise 并使两个兼容类型的值。 下面是
JavaScript 中检查函数是否返回的 Promise
发布时间:2022/12/15 浏览次数:556 分类:JavaScript
-
要检查函数是否返回 Promise ,请检查函数是否异步或调用它并检查函数是否返回具有函数类型 then 属性的对象。 如果满足任一条件,该函数将返回一个 Promise 。 // ✅ Promise check functio
JavaScript 中 Promise.resolve is not a constructor 错误
发布时间:2022/12/02 浏览次数:212 分类:JavaScript
-
当我们尝试将 Promise.resolve() 方法与 new 运算符一起使用时,会出现Promise.resolve is not a constructor错误。 Promise.resolve() 方法不是构造函数,因此应该在没有 new 运算符的情况下使用它,例如
JavaScript 中如何访问 Promise 的值
发布时间:2022/12/02 浏览次数:75 分类:JavaScript
-
使用 Promise.then() 方法访问承诺的值,例如 p.then(value = console.log(value)) 。 then() 方法接受一个函数,该函数将 promise 的解析值作为参数传递。 // ?️ Example promise const p = Promise . resolve (
JavaScript 中检查函数是否返回 Promise
发布时间:2022/12/02 浏览次数:70 分类:JavaScript
-
要检查函数是否返回 Promise ,请检查函数是否异步或调用它并检查函数是否返回具有函数类型 then 属性的对象。 如果满足任一条件,该函数将返回一个 Promise 。 // ✅ Promise check functio