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 弹出框 替代浏览器的弹出框
Oct 29 Javascript
jquery ui dialog实现弹窗特效的思路及代码
Aug 03 Javascript
jquery的选择器的使用技巧之如何选择input框
Sep 22 Javascript
JS获取农历日期具体实例
Nov 14 Javascript
jquery模拟LCD 时钟的html文件源代码
Jun 16 Javascript
JS实现弹性菜单效果代码
Sep 07 Javascript
js 调用百度分享功能
Feb 27 Javascript
Vue插件写、用详解(附demo)
Mar 20 Javascript
ES6 javascript中Class类继承用法实例详解
Oct 30 Javascript
详解webpack+express多页站点开发
Dec 22 Javascript
angular4 JavaScript内存溢出问题
Mar 06 Javascript
环形加载进度条封装(Vue插件版和原生js版)
Dec 04 Javascript
一些可能会用到的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
PHP 时间转换Unix时间戳代码
2010/01/22 PHP
PHP 如何获取二维数组中某个key的集合
2014/06/03 PHP
Yii Framework框架获取分类下面的所有子类方法
2014/06/20 PHP
PHP内置过滤器FILTER使用实例
2014/06/25 PHP
Centos PHP 扩展Xchche的安装教程
2016/07/09 PHP
thinkphp的dump函数无输出实例代码
2016/11/15 PHP
PHP创建XML的方法示例【基于DOMDocument类及SimpleXMLElement类】
2019/09/10 PHP
将HTML自动转为JS代码
2006/06/26 Javascript
解决 FireFox 下[使用event很麻烦] 的问题.
2006/08/22 Javascript
ExtJs3.0中Store添加 baseParams 的Bug
2010/03/10 Javascript
Json实现异步请求提交评论无需跳转其他页面
2014/10/11 Javascript
javascript实现根据iphone屏幕方向调用不同样式表的方法
2015/07/13 Javascript
jQuery常用知识点总结以及平时封装常用函数
2016/02/23 Javascript
Node.js Sequelize如何实现数据库的读写分离
2016/10/23 Javascript
vue中添加mp3音频文件的方法
2018/03/02 Javascript
通过vue提供的keep-alive减少对服务器的请求次数
2018/04/01 Javascript
vue根据进入的路由进行原路返回的方法
2018/09/26 Javascript
ES6 let和const定义变量与常量的应用实例分析
2019/06/27 Javascript
layui表单验证select下拉框实现验证的方法
2019/09/05 Javascript
vue style width a href动态拼接问题的解决
2020/08/07 Javascript
[52:26]完美世界DOTA2联赛决赛 FTD vs Phoenix 第一场 11.08
2020/11/11 DOTA
python通过openpyxl生成Excel文件的方法
2015/05/12 Python
分享几道你可能遇到的python面试题
2017/07/24 Python
Python Nose框架编写测试用例方法
2017/10/26 Python
Python数据结构之哈夫曼树定义与使用方法示例
2018/04/22 Python
CentOS7下安装python3.6.8的教程详解
2020/01/03 Python
英国网上香水店:Fragrance Direct
2016/07/20 全球购物
女孩每月服装订阅盒:kidpik
2019/04/17 全球购物
彪马西班牙官网:PUMA西班牙
2019/06/18 全球购物
怎样在程序里获得一个空指针
2015/01/24 面试题
运动会广播稿200字
2014/01/15 职场文书
班组拓展活动方案
2014/08/14 职场文书
关于十八大的演讲稿
2014/09/15 职场文书
学生检讨书如何写
2014/10/30 职场文书
MySQL 使用SQL语句修改表名的实现
2021/04/07 MySQL
MySQL磁盘碎片整理实例演示
2022/04/03 MySQL