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 相关文章推荐
Jquery通过Ajax方式来提交Form表单的具体实现
Nov 07 Javascript
js和jquery中循环的退出和继续学习记录
Sep 06 Javascript
JavaScript中实现PHP的打乱数组函数shuffle实例
Oct 11 Javascript
Ionic如何创建APP项目
Jun 03 Javascript
理解AngularJs篇:30分钟快速掌握AngularJs
Dec 23 Javascript
[js高手之路]从原型链开始图解继承到组合继承的产生详解
Aug 28 Javascript
Bootstrap 模态框多次显示后台提交多次BUG的解决方法
Dec 26 Javascript
利用vscode调试编译后的js代码详解
May 14 Javascript
JavaScript解析及序列化JSON的方法实例分析
Jan 04 Javascript
vue $set 给数据赋值的实例
Nov 09 Javascript
详解用js代码触发dom事件的实现方案
Jun 10 Javascript
vue使用openlayers实现移动点动画
Sep 24 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中echo()和print()、require()和include()等易混淆函数的区别
2012/02/22 PHP
基于PHP常用函数的用法详解
2013/05/10 PHP
php去除HTML标签实例
2013/11/06 PHP
PHP中把有符号整型转换为无符号整型方法
2015/05/27 PHP
了解PHP的返回引用和局部静态变量
2015/06/04 PHP
php版微信自定义回复功能示例
2016/12/05 PHP
PHP基于GD库实现的生成图片缩略图函数示例
2017/07/05 PHP
PHP中in_array的隐式转换的解决方法
2018/03/06 PHP
php curl简单采集图片生成base64编码(并附curl函数参数说明)
2019/02/15 PHP
很全的显示阴历(农历)日期的js代码
2009/01/01 Javascript
jquery ready(fn)事件使用介绍
2013/08/21 Javascript
js 判断浏览器使用的语言示例代码
2014/03/22 Javascript
Bootstrap基本插件学习笔记之模态对话框(16)
2016/12/08 Javascript
简单谈谈Javascript函数中的arguments
2017/02/09 Javascript
jquery实现数字输入框
2017/02/22 Javascript
基于JavaScript实现百度搜索框效果
2020/06/28 Javascript
简述Angular 5 快速入门
2017/11/04 Javascript
JS+HTML5 canvas绘制验证码示例
2018/12/05 Javascript
微信小程序实现原生步骤条
2019/07/25 Javascript
node.js如何操作MySQL数据库
2020/10/29 Javascript
[46:48]DOTA2上海特级锦标赛A组小组赛#2 Secret VS CDEC第三局
2016/02/25 DOTA
Python实现TCP协议下的端口映射功能的脚本程序示例
2016/06/14 Python
windows10系统中安装python3.x+scrapy教程
2016/11/08 Python
python 系统调用的实例详解
2017/07/11 Python
TensorFlow损失函数专题详解
2018/04/26 Python
Python通过Manager方式实现多个无关联进程共享数据的实现
2019/11/07 Python
python Opencv计算图像相似度过程解析
2019/12/03 Python
Window版下在Jupyter中编写TensorFlow的环境搭建
2020/04/10 Python
windows python3安装Jupyter Notebooks教程
2020/04/13 Python
彻底解决Python包下载慢问题
2020/11/15 Python
COSETTE官网:奢华,每天
2020/03/22 全球购物
优秀团队获奖感言
2014/02/19 职场文书
做一个有道德的人活动实施方案
2014/08/23 职场文书
行政执法队伍作风整顿剖析材料
2014/10/11 职场文书
《黑岩★★射手 DAWN FALL》BD发售宣传CM公开
2022/04/04 日漫
Redis唯一ID生成器的实现
2022/07/07 Redis