Async/Await替代Promise的6个理由


Posted in Javascript onJune 15, 2019

前言

Node.js 7.6 已经支持 async/await 了,如果你还没有试过,这篇博客将告诉你为什么要用它。

Async/Await 简介

对于从未听说过 async/await 的朋友,下面是简介:

  • async/await 是写异步代码的新方式,以前的方法有回调函数和Promise。
  • async/await 是基于 Promise 实现的,它不能用于普通的回调函数。
  • async/await 与 Promise 一样,是非阻塞的。
  • async/await 使得异步代码看起来像同步代码,这正是它的魔力所在。

Async/Await 语法

示例中,getJSON 函数返回一个 promise,这个 promise 成功 resolve 时会返回一个 json 对象。我们只是调用这个函数,打印返回的 JSON 对象,然后返回”done”。

使用 Promise 是这样的:

const makeRequest = () =>
getJSON().then(data => {
console.log(data);
return "done";
});
makeRequest();

使用 Async/Await 是这样的:

const makeRequest = async () => {
console.log(await getJSON());
return "done";
};
makeRequest();

它们有一些细微不同:

函数前面多了一个 async 关键字。await 关键字只能用在 async 定义的函数内。async 函数会隐式地返回一个 promise,该 promise 的 reosolve 值就是函数 return 的值。(示例中 reosolve 值就是字符串”done”)

第 1 点暗示我们不能在最外层代码中使用 await,因为不在 async 函数内。

// 不能在最外层代码中使用await
await makeRequest();
// 这是会出事情的
makeRequest().then(result => {
// 代码
});

await getJSON()表示 console.log 会等到 getJSON 的 promise 成功 reosolve 之后再执行。

为什么 Async/Await 更好?

1. 简洁

由示例可知,使用 Async/Await 明显节约了不少代码。我们不需要写.then,不需要写匿名函数处理 Promise 的 resolve 值,也不需要定义多余的 data 变量,还避免了嵌套代码。这些小的优点会迅速累计起来,这在之后的代码示例中会更加明显。

2. 错误处理

Async/Await 让 try/catch 可以同时处理同步和异步错误。在下面的 promise 示例中,try/catch 不能处理 JSON.parse 的错误,因为它在 Promise 中。我们需要使用.catch,这样错误处理代码非常冗余。并且,在我们的实际生产代码会更加复杂。

const makeRequest = () => {
try {
getJSON().then(result => {
// JSON.parse可能会出错
const data = JSON.parse(result);
console.log(data);
});
// 取消注释,处理异步代码的错误
// .catch((err) => {
// console.log(err)
// })
} catch (err) {
console.log(err);
}
};

使用 async/await 的话,catch 能处理 JSON.parse 错误:

const makeRequest = async () => {
try {
// this parse may fail
const data = JSON.parse(await getJSON());
console.log(data);
} catch (err) {
console.log(err);
}
};

3. 条件语句

下面示例中,需要获取数据,然后根据返回数据决定是直接返回,还是继续获取更多的数据。

const makeRequest = () => {
return getJSON().then(data => {
if (data.needsAnotherRequest) {
return makeAnotherRequest(data).then(moreData => {
console.log(moreData);
return moreData;
});
} else {
console.log(data);
return data;
}
});
};

这些代码看着就头痛。嵌套(6 层),括号,return 语句很容易让人感到迷茫,而它们只是需要将最终结果传递到最外层的 Promise。

上面的代码使用 async/await 编写可以大大地提高可读性:

const makeRequest = async () => {
const data = await getJSON();
if (data.needsAnotherRequest) {
const moreData = await makeAnotherRequest(data);
console.log(moreData);
return moreData;
} else {
console.log(data);
return data;
}
};

4. 中间值

你很可能遇到过这样的场景,调用 promise1,使用 promise1 返回的结果去调用 promise2,然后使用两者的结果去调用 promise3。你的代码很可能是这样的:

const makeRequest = () => {
return promise1().then(value1 => {
return promise2(value1).then(value2 => {
return promise3(value1, value2);
});
});
};

如果 promise3 不需要 value1,可以很简单地将 promise 嵌套铺平。如果你忍受不了嵌套,你可以将 value 1 & 2 放进 Promise.all 来避免深层嵌套:

const makeRequest = () => {
return promise1()
.then(value1 => {
return Promise.all([value1, promise2(value1)]);
})
.then(([value1, value2]) => {
return promise3(value1, value2);
});
};

这种方法为了可读性牺牲了语义。除了避免嵌套,并没有其他理由将 value1 和 value2 放在一个数组中。

使用 async/await 的话,代码会变得异常简单和直观。

const makeRequest = async () => {
const value1 = await promise1();
const value2 = await promise2(value1);
return promise3(value1, value2);
};

5. 错误栈

下面示例中调用了多个 Promise,假设 Promise 链中某个地方抛出了一个错误:

const makeRequest = () => {
return callAPromise()
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => callAPromise())
.then(() => {
throw new Error("oops");
});
};
makeRequest().catch(err => {
console.log(err);
// output
// Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
});

Promise 链中返回的错误栈没有给出错误发生位置的线索。更糟糕的是,它会误导我们;错误栈中唯一的函数名为 callAPromise,然而它和错误没有关系。(文件名和行号还是有用的)。

然而,async/await 中的错误栈会指向错误所在的函数:

const makeRequest = async () => {
await callAPromise();
await callAPromise();
await callAPromise();
await callAPromise();
await callAPromise();
throw new Error("oops");
};
makeRequest().catch(err => {
console.log(err);
// output
// Error: oops at makeRequest (index.js:7:9)
});

在开发环境中,这一点优势并不大。但是,当你分析生产环境的错误日志时,它将非常有用。这时,知道错误发生在 makeRequest 比知道错误发生在 then 链中要好。

6. 调试

最后一点,也是非常重要的一点在于,async/await 能够使得代码调试更简单。2 个理由使得调试 Promise 变得非常痛苦:

不能在返回表达式的箭头函数中设置断点

如果你在.then 代码块中设置断点,使用 Step Over 快捷键,调试器不会跳到下一个.then,因为它只会跳过异步代码。
使用 await/async 时,你不再需要那么多箭头函数,这样你就可以像调试同步代码一样跳过 await 语句。

结论

Async/Await 是近年来 JavaScript 添加的最革命性的特性之一。它会让你发现 Promise 的语法有多糟糕,而且提供了一个直观的替代方法。

忧虑

对于 Async/Await,也许你有一些合理的怀疑:

它使得异步代码不再明显: 我们已经习惯了用回调函数或者.then 来识别异步代码,我们可能需要花数个星期去习惯新的标志。但是,C#拥有这个特性已经很多年了,熟悉它的朋友应该知道暂时的稍微不方便是值得的。
Node 7 不是 LTS(长期支持版本): 但是,Node 8 下个月就会发布,将代码迁移到新版本会非常简单。(Fundebug 注:Node 8 是 LTS,已经于 2017 年 10 月正式发布。)

原文: 6 Reasons Why JavaScript's Async/Await Blows Promises Away

译者: Fundebug

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js实现addClass,removeClass,hasClass的函数代码
Jul 13 Javascript
解析Jquery取得iframe中元素的几种方法
Jul 04 Javascript
JS正则表达式大全(整理详细且实用)
Nov 14 Javascript
jQuery.form插件的使用及跨域异步上传文件
Apr 27 Javascript
JS 实现导航菜单中的二级下拉菜单的几种方式
Oct 31 Javascript
node.js Sequelize实现单实例字段或批量自增、自减
Dec 08 Javascript
javascript 使用正则test( )第一次是 true,第二次是false
Feb 22 Javascript
js实现登录框鼠标拖拽效果
Mar 09 Javascript
jQuery动态追加页面数据以及事件委托详解
May 06 jQuery
JavaScript命名空间模式实例详解
Jun 20 Javascript
如何用JavaScript学习算法复杂度
Apr 30 Javascript
Vue ECharts实现机舱座位选择展示功能
May 15 Vue.js
一些可能会用到的Node.js面试题
Jun 15 #Javascript
使用Vue.js 和Chart.js制作绚丽多彩的图表
Jun 15 #Javascript
通过实例讲解JS如何防抖动
Jun 15 #Javascript
js笔试题-接收get请求参数
Jun 15 #Javascript
深入了解JavaScript 的 WebAssembly
Jun 15 #Javascript
通过实例了解js函数中参数的传递
Jun 15 #Javascript
RxJS的入门指引和初步应用
Jun 15 #Javascript
You might like
10 个经典PHP函数
2013/10/17 PHP
php框架CI(codeigniter)自动加载与自主创建对象操作实例分析
2020/06/06 PHP
php+mysql+ajax 局部刷新点赞/取消点赞功能(每个账号只点赞一次)
2020/07/24 PHP
div拖拽插件——JQ.MoveBox.js(自制JQ插件)
2013/05/17 Javascript
Ext中下拉列表ComboBox组件store数据格式用法介绍
2013/07/15 Javascript
jquery弹出层类代码分享
2013/12/27 Javascript
jquery跨域请求示例分享(jquery发送ajax请求)
2014/03/25 Javascript
JS获取当前日期时间并定时刷新示例
2021/03/04 Javascript
JS实现在状态栏显示打字效果完整实例
2015/11/02 Javascript
Express实现前端后端通信上传图片之存储数据库(mysql)傻瓜式教程(二)
2015/12/10 Javascript
Angular.js实现注册系统的实例详解
2016/12/18 Javascript
基于JavaScript实现下拉列表左右移动代码
2017/02/07 Javascript
JS中去掉array中重复元素的方法
2017/05/26 Javascript
React中jquery引用的实现方法
2017/09/12 jQuery
JS扩展String.prototype.format字符串拼接的功能
2018/03/09 Javascript
微信小程序使用map组件实现获取定位城市天气或者指定城市天气数据功能
2019/01/22 Javascript
浅谈Vue为什么不能检测数组变动
2019/10/14 Javascript
javascript中的with语句学习笔记及用法
2020/02/17 Javascript
[01:06:26]全国守擂赛第二周 Team Coach vs DeMonsTer
2020/04/28 DOTA
python搜索指定目录的方法
2015/04/29 Python
Python实现小数转化为百分数的格式化输出方法示例
2017/09/20 Python
numpy使用技巧之数组过滤实例代码
2018/02/03 Python
对tensorflow 的模型保存和调用实例讲解
2018/07/28 Python
selenium+python自动化测试环境搭建步骤
2019/06/03 Python
numpy和pandas中数组的合并、拉直和重塑实例
2019/06/28 Python
python 用所有标点符号分隔句子的示例
2019/07/15 Python
Django ModelForm操作及验证方式
2020/03/30 Python
委托书样本
2014/04/02 职场文书
一年级学生评语
2014/04/23 职场文书
2015年班级元旦晚会活动总结
2014/11/28 职场文书
学习雷锋精神活动总结
2015/02/06 职场文书
环境保护宣传标语大全!
2019/06/28 职场文书
python cv2图像质量压缩的算法示例
2021/06/04 Python
pycharm代码删除恢复的方法
2021/06/26 Python
详解Go语言Slice作为函数参数的使用
2021/07/02 Golang
Nginx使用Lua模块实现WAF的原理解析
2021/09/04 Servers