【悄咪咪学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 例子,我们来看一下它的结构

js 复制代码
// 定义一个异步方法,返回一个 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 标识符,可以将方法定义为异步函数。

那么异步函数究竟是什么东西呢?我们打印看一下。

js 复制代码
async function asyncFn() {
    return 'none JS';
}

console.log(asyncFn());

结果

shell 复制代码
Promise {<resolved>: "none JS"}

Tips:使用 async 标识符定义的异步函数,就是在该函数外包裹一层 Promise,并将该函数的返回值当作 Promise 已完成 状态的返回值。

2.4 await 等待运算符

await 是一个运算符,其功能是等待 任意表达式Promise 结果。

await 运算符必须使用在 异步函数 内部。

如果使用 await 标识一个普通表达式,效果不会有什么变化。

看一下 结构预览 中的例子的执行结果:

shell 复制代码
before promise await
promise await
after promise await

如果将 结构预览 中的例子中第十五行的 await 运算符去掉,执行结果却是:

shell 复制代码
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

js 复制代码
task1()
    .then(task2)
    .then(task3)
    .then(task4)
    .then(task5);

async/await

js 复制代码
await task1();
await task2();
await task3();
await task4();
await task5();

在这个简单的对比中,async/await 写法明显缩进更少、相比于 Promise 的链式回调,也更容易让人理解。

4. 怎么用 async/await 优化 Promise 编码

读到这里,可能很多人觉得"如何使用async/await优化 Promise 编码"这个命题十分简单,认为去掉.then(),用 await 等待 Proimse 执行,再变量接住返回值就好了。比如:

js 复制代码
function wait(ms) {
    return new Promise(function(resolve, reject) {
        setTimeout(resolve, ms, true);
    });
}

Promise

js 复制代码
let wait1 = wait(1000),
    wait2 = wait(2000);
    
Promise.all([wait1, wait2])
    .then(function() {
        console.log('complete');
    })

使用 async/await 改造后,绝对不是:

js 复制代码
await wait(1000);
await wait(2000);

console.log('complete');

而应该是:

js 复制代码
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

js 复制代码
let wait1 = wait(1000),
    wait2 = wait(2000);
    
Promise.all([wait1, wait2])
    .then(function() {
        console.log('complete');
    })

async/await

js 复制代码
let wait1 = wait(1000),
    wait2 = wait(2000);
    
await Promise.all([wait1, wait2]);
console.log('complete');

Promise

js 复制代码
let wait1 = wait(1000),
    wait2 = wait(2000);
    
wait1
    .then(wait2)
    .then(function() {
        console.log('complete');
    })

async/await

js 复制代码
await wait(1000);
await wait(2000);

console.log('complete');

5. 小结

本节课程我们主要学习了 async/await 是什么async/await 解决了什么问题async/await 怎么使用

重点如下:

  1. 重点1

    async/await 是一颗语法糖,通过 Promise 实现。其最终目标是 异步扁平化

  2. 重点2

    async 能标识一个函数为 异步函数,即在该函数外部包裹一层 Promise。

    await 是一个运算符,它能等待一个表达式 或 Promise 的结果。

    await 运算符必须要在 异步函数 中使用。

  3. 重点3

    使用 async/await 来优化已存在的 Promise 代码时,必须要先理清原本的流程控制,将可异步的保持异步,有上下文依赖关系的坚决保持同步。否则将迎来 负优化

相关推荐
万少27 分钟前
万少用9个AI工具,帮朋友完成了一个"不可能"的项目
前端
小小小小宇30 分钟前
Vue `import` 为什么可以异步加载
前端
WMYeah35 分钟前
【无标题】
前端·rust·抽奖程序·跨平台抽奖程序
Unbelievabletobe35 分钟前
免费外汇api的响应时间在不同时段下的波动分析
大数据·开发语言·前端·python
大哥,带带弟弟1 小时前
Grafana 前端嵌入与 JWT 鉴权实战
前端·grafana
小小小小宇1 小时前
前端 V8 引擎垃圾回收机制与内存问题排查
前端
前端老石人1 小时前
CSS 值定义语法
前端·css
sheeta19981 小时前
Vue 前端基础笔记
前端·vue.js·笔记
小小小小宇1 小时前
GitLab + GitLab Runner + Qiankun 微前端 + Nginx + Node 中间件 前端开发机从零搭建 CI/CD 全流程
前端
前端那点事1 小时前
别再写垃圾组件!Vue3 如何设计「真正可复用」的高质量通用组件
前端·vue.js