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

相关推荐
AiMan37 分钟前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真40 分钟前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment42 分钟前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan1 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么1 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技1 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥2 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易2 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭3 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter
严肃小白兔3 小时前
TanStack Router 更新构建工具链:Node.js 兼容性、路由分割与前端工程实践
前端·node.js·状态模式