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 相关文章推荐
javascript不可用的问题探究
Oct 01 Javascript
jQuery性能优化的38个建议
Mar 04 Javascript
jQuery中attr()和prop()在修改checked属性时的区别
Jul 18 Javascript
原生javascript实现图片按钮切换
Jan 12 Javascript
jQuery遍历DOM的父级元素、子级元素和同级元素的方法总结
Jul 07 Javascript
javascript删除html标签函数cIsHTML
Jan 09 Javascript
thinkjs之页面跳转同步异步操作
Feb 05 Javascript
微信小程序 基础知识css样式media标签
Feb 15 Javascript
JS模拟超市简易收银台小程序代码解析
Aug 18 Javascript
JavaScript遍历数组的三种方法map、forEach与filter实例详解
Feb 27 Javascript
微信小程序bindtap事件与冒泡阻止详解
Aug 08 Javascript
vue实现路由监听和参数监听
Oct 29 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获取微信共享收货地址的方法
2017/12/21 PHP
json-lib出现There is a cycle in the hierarchy解决办法
2010/02/24 Javascript
js数组操作学习总结
2013/11/04 Javascript
jqGrid增加时--判断开始日期与结束日期(实例解析)
2013/11/08 Javascript
js简单实现标签云效果实例
2015/08/06 Javascript
Bootstrap布局组件教程之Bootstrap下拉菜单
2016/06/12 Javascript
javascript加减乘除的简单实例
2016/07/12 Javascript
清除浏览器缓存的几种方法总结(必看)
2016/12/09 Javascript
three.js实现围绕某物体旋转
2017/01/25 Javascript
Bootstrap入门教程一Hello Bootstrap初识
2017/03/02 Javascript
详解Vue2 无限级分类(添加,删除,修改)
2017/03/07 Javascript
vue2.0 路由不显示router-view的解决方法
2018/03/06 Javascript
JavaScript事件委托原理与用法实例分析
2018/06/07 Javascript
vue中的mvvm模式讲解
2019/01/31 Javascript
微信小程序Flex布局用法深入浅出分析
2019/04/25 Javascript
[01:34]2016国际邀请赛中国区预选赛IG战队教练采访
2016/06/27 DOTA
python获取网页状态码示例
2014/03/30 Python
决策树的python实现方法
2014/11/18 Python
Python实现ping指定IP的示例
2018/06/04 Python
django 外键model的互相读取方法
2018/12/15 Python
python自动化UI工具发送QQ消息的实例
2019/08/27 Python
打包PyQt5应用时的注意事项
2020/02/14 Python
对python中list的五种查找方法说明
2020/07/13 Python
Python图像读写方法对比
2020/11/16 Python
如何用Python徒手写线性回归
2021/01/25 Python
巴西图书和电子产品购物网站:Saraiva
2017/06/07 全球购物
美国在线和移动免费会员制批发零售商:Boxed(移动端的Costco)
2020/01/02 全球购物
自立自强的名人事例
2014/02/10 职场文书
2014年小班元旦活动方案
2014/02/16 职场文书
2014年迎新年活动方案
2014/02/19 职场文书
五年后的职业生涯规划
2014/03/04 职场文书
机关党员2014全国两会学习心得体会
2014/03/10 职场文书
党员教师批评与自我批评发言稿
2014/10/15 职场文书
2015年幼儿园大班工作总结
2015/04/25 职场文书
java实现对Hadoop的操作
2021/07/01 Java/Android
MySQL数据库如何查看表占用空间大小
2022/06/10 MySQL