JS异步堆栈追踪之为什么await胜过Promise


Posted in Javascript onApril 28, 2021

概述

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传输。

以上就是JS异步堆栈追踪之为什么await胜过Promise的详细内容,更多关于Javascript的资料请关注三水点靠木其它相关文章!

Javascript 相关文章推荐
js post方式传递提交的实现代码
May 31 Javascript
javascript中的nextSibling使用陷(da)阱(keng)
May 05 Javascript
jQuery判断当前点击的是第几个li的代码
Sep 26 Javascript
javascript最基本的函数汇总
Jun 25 Javascript
JS实现灵巧的下拉导航效果代码
Aug 25 Javascript
解决angular的$http.post()提交数据时后台接收不到参数值问题的方法
Dec 10 Javascript
js实现当鼠标移到表格上时显示这一格全部内容的代码
Jun 12 Javascript
jQuery实现表格行和列的动态添加与删除方法【测试可用】
Aug 01 Javascript
Bootstrap栅格系统的使用和理解2
Dec 14 Javascript
three.js加载obj模型的实例代码
Nov 10 Javascript
vue获取当前点击的元素并传值的实例
Mar 09 Javascript
js实现旋转木马轮播图效果
Jan 10 Javascript
vue引入Excel表格插件的方法
Apr 28 #Vue.js
react如何快速设置文件路径别名
原生JS封装vue Tab切换效果
vue项目两种方式实现竖向表格的思路分析
测量JavaScript函数的性能各种方式对比
Apr 27 #Javascript
比较node.js和Deno
Apr 27 #Javascript
如何用JavaScript检测当前浏览器是无头浏览器
Apr 27 #Javascript
You might like
DOTA2【瓜皮时刻】Vol.91 RTZ山史最惨“矿难”
2021/03/05 DOTA
smarty+adodb+部分自定义类的php开发模式
2006/12/31 PHP
php 购物车实例(申精)
2009/05/11 PHP
php-cli简介(不会Shell语言一样用Shell)
2013/06/03 PHP
extjs DataReader、JsonReader、XmlReader的构造方法
2009/11/07 Javascript
Javascript中的变量使用说明
2010/05/18 Javascript
基于JavaScript实现继承机制之原型链(prototype chaining)的详解
2013/05/07 Javascript
如何实现修改密码时密码框显示保存到cookie的密码
2013/12/10 Javascript
使用jquery选择器如何获取父级元素、同级元素、子元素
2014/05/14 Javascript
jQuery制作可自定义大小的拼图游戏
2015/03/30 Javascript
jQuery实现首页图片淡入淡出效果的方法
2015/06/10 Javascript
利用jQuery实现漂亮的圆形进度条倒计时插件
2015/09/30 Javascript
jquery实现页面常用的返回顶部效果
2016/03/04 Javascript
jQuery基于扩展简单实现倒计时功能的方法
2016/05/14 Javascript
网页挂马方式整理及详细介绍
2016/11/03 Javascript
jquery做个日期选择适用于手机端示例
2017/01/10 Javascript
bootstrapValidator 重新启用提交按钮的方法
2017/02/20 Javascript
Vue中添加过渡效果的方法
2017/03/16 Javascript
原生js实现仿window10系统日历效果的实例
2017/10/31 Javascript
vue 使用自定义指令实现表单校验的方法
2018/08/28 Javascript
vue2.0 实现富文本编辑器功能
2019/05/26 Javascript
JavaScript canvas实现文字时钟
2021/01/10 Javascript
基于vue-simple-uploader封装文件分片上传、秒传及断点续传的全局上传插件功能
2021/02/23 Vue.js
[01:19]2014DOTA2国际邀请赛 采访TITAN战队ohaiyo 能赢DK很幸运
2014/07/12 DOTA
使用PYTHON创建XML文档
2012/03/01 Python
Django 对象关系映射(ORM)源码详解
2019/08/06 Python
python实现的读取网页并分词功能示例
2019/10/29 Python
python3.5的包存放的具体路径
2020/08/16 Python
BeautifulSoup中find和find_all的使用详解
2020/12/07 Python
惊艳的手工时装首饰:Migonne Gavigan
2018/02/23 全球购物
护理专业的自荐信
2013/10/22 职场文书
会计专业自荐信
2014/06/03 职场文书
个人合伙协议书范本
2014/10/14 职场文书
成都人事代理协议书
2014/10/25 职场文书
Filebeat 采集 Nginx 日志的方法
2021/03/31 Servers
图解排序算法之希尔排序Java实现
2021/06/26 Java/Android