【悄咪咪学Node.js】5. 语法糖 async/await
async/await
1. 前言
本节课将会引导大家学习了解:
- async/await 是什么
- async/await 为什么会出现
- 怎么正确使用 async/await
学习完本节课程后,应该具有:
- 使用 async/await 改造 Promise 控制执行流程的能力
- 从零开始编写使用 async/await 的异步函数的能力
- 判断 await 运算符该不该使用的能力
2. async/await 是什么
2.1 笔者解释
async/await 是在 Node.js 7.6 版本及以上支持的 JavaScript ES7 特性。
async/await 是 Promise 的语法糖,其本质上就是 Promise。
async/await 是 异步扁平化 的最终手段,可以让你轻松写出同步风格的代码同时又拥有异步机制,更加简洁,逻辑更加清晰。
2.2 结构预览
先给一个直观的 async/await 例子,我们来看一下它的结构
// 定义一个异步方法,返回一个 promise,1 秒后将 promise 状态修改为 已完成。
function promiseFn() {
return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('promise await');
resolve();
}, 1000);
})
}
// 用 async 标识标记该方法为异步方法
async function asyncFn() {
console.log('before promise await');
// 用 await 标识,强调等待后面的 promise 执行完成。
await promiseFn();
console.log('after promise await');
}
下面分别介绍一下 async、await 的具体意思和使用方法。
2.3 async function 声明异步函数
在定义函数时,使用 async 标识符,可以将方法定义为异步函数。
那么异步函数究竟是什么东西呢?我们打印看一下。
async function asyncFn() {
return 'none JS';
}
console.log(asyncFn());
结果
Promise {<resolved>: "none JS"}
Tips:使用 async 标识符定义的异步函数,就是在该函数外包裹一层 Promise,并将该函数的返回值当作 Promise 已完成 状态的返回值。
2.4 await 等待运算符
await 是一个运算符,其功能是等待 任意表达式 或 Promise 结果。
await 运算符必须使用在 异步函数 内部。
如果使用 await 标识一个普通表达式,效果不会有什么变化。
看一下 结构预览 中的例子的执行结果:
before promise await
promise await
after promise await
如果将 结构预览 中的例子中第十五行的 await 运算符去掉,执行结果却是:
before promise await
after promise await
promise await
由此可见,通过添加 await 运算符的确能有效等待 Promise 执行完成。
2.5 结论
async/await 没有增加新的机制,而是通过 Promise 实现。
async 能标识函数成为 异步函数,其实际就是在该函数外包裹一层 Promise,并将函数的返回值作为 Promise 已完成状态的返回值。
同理,在 异步函数 中抛出的异常,也会将 Promise 修改为已拒绝状态,并把异常作为已拒绝状态的返回值。
await 是一个运算符,其对普通表达式无显式影响,却能等待 Promise 对象完成。
3. async/await 解决了什么问题
3.1 异步扁平化
async/await 能将异步逻辑写成同步逻辑的代码风格,我们对比一下 async/await 和 Promise 的编写效果。
Promise
task1()
.then(task2)
.then(task3)
.then(task4)
.then(task5);
async/await
await task1();
await task2();
await task3();
await task4();
await task5();
在这个简单的对比中,async/await 写法明显缩进更少、相比于 Promise 的链式回调,也更容易让人理解。
4. 怎么用 async/await 优化 Promise 编码
读到这里,可能很多人觉得“如何使用async/await优化 Promise 编码”这个命题十分简单,认为去掉.then()
,用 await 等待 Proimse 执行,再变量接住返回值就好了。比如:
function wait(ms) {
return new Promise(function(resolve, reject) {
setTimeout(resolve, ms, true);
});
}
Promise
let wait1 = wait(1000),
wait2 = wait(2000);
Promise.all([wait1, wait2])
.then(function() {
console.log('complete');
})
使用 async/await 改造后,绝对不是:
await wait(1000);
await wait(2000);
console.log('complete');
而应该是:
let wait1 = wait(1000),
wait2 = wait(2000);
await Promise.all([wait1, wait2]);
console.log('complete');
使用 async/await 对 Promise 编码进行 不当优化 的结果:
- 轻则由于完全不允许异步,使得 Node.js 失去处理高并发的优势
- 重则由于更改了原有流程控制,使得程序执行异常
Tips: 使用 async/await 改造 Promise 的一个重点是:理解清楚本来的代码流程,将可异步的保持异步,有上下文依赖关系的坚决保持同步。
4.1 代码例子
下面列举几种情况,供大家思考:
Promise
let wait1 = wait(1000),
wait2 = wait(2000);
Promise.all([wait1, wait2])
.then(function() {
console.log('complete');
})
async/await
let wait1 = wait(1000),
wait2 = wait(2000);
await Promise.all([wait1, wait2]);
console.log('complete');
Promise
let wait1 = wait(1000),
wait2 = wait(2000);
wait1
.then(wait2)
.then(function() {
console.log('complete');
})
async/await
await wait(1000);
await wait(2000);
console.log('complete');
5. 小结
本节课程我们主要学习了 async/await 是什么、async/await 解决了什么问题、async/await 怎么使用。
重点如下:
-
重点1
async/await 是一颗语法糖,通过 Promise 实现。其最终目标是 异步扁平化。
-
重点2
async 能标识一个函数为 异步函数,即在该函数外部包裹一层 Promise。
await 是一个运算符,它能等待一个表达式 或 Promise 的结果。
await 运算符必须要在 异步函数 中使用。
-
重点3
使用 async/await 来优化已存在的 Promise 代码时,必须要先理清原本的流程控制,将可异步的保持异步,有上下文依赖关系的坚决保持同步。否则将迎来 负优化。
转载自:https://juejin.cn/post/7350277481943384101