【悄咪咪学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 代码时,必须要先理清原本的流程控制,将可异步的保持异步,有上下文依赖关系的坚决保持同步。否则将迎来 负优化

相关推荐
挣扎与觉醒中的技术人20 分钟前
【技术干货】三大常见网络攻击类型详解:DDoS/XSS/中间人攻击,原理、危害及防御方案
前端·网络·ddos·xss
zeijiershuai24 分钟前
Vue框架
前端·javascript·vue.js
写完这行代码打球去26 分钟前
没有与此调用匹配的重载
前端·javascript·vue.js
华科云商xiao徐26 分钟前
使用CPR库编写的爬虫程序
前端
狂炫一碗大米饭29 分钟前
Event Loop事件循环机制,那是什么事件?又是怎么循环呢?
前端·javascript·面试
IT、木易30 分钟前
大白话Vue Router 中路由守卫(全局守卫、路由独享守卫、组件内守卫)的种类及应用场景
前端·javascript·vue.js
顾林海31 分钟前
JavaScript 变量与常量全面解析
前端·javascript
程序员小续31 分钟前
React 组件库:跨版本兼容的解决方案!
前端·react.js·面试
乐坏小陈32 分钟前
2025 年你希望用到的现代 JavaScript 模式 【转载】
前端·javascript
生在地上要上天33 分钟前
从600行"状态地狱"到可维护策略模式:一次列表操作限制重构实践
前端