Menu
CoddyTech

Async 和 Await

本课属于 Coddy Node.js 学习旅程的“Node.js 基础”单元。第 23 课,共 33 课。

一连串 .then 调用的阅读顺序,和你对步骤的理解不同:读取,然后解析,再保存。

在 async 函数中,await 会暂停执行,直到 promise 完成,并返回它的值。如果 promise 失败,就会在此处抛出异常,因此可以用 try 和 catch 处理。在模块中,也可以在顶层使用 await。

import { readFile } from 'node:fs/promises';

const loadConfig = async () => {
    try {
        return JSON.parse(await readFile('config.json', 'utf8'));
    } catch {
        return {};
    }
};

const config = await loadConfig();
console.log(config.port ?? 3000);

async 函数总是返回一个 promise,所以调用它的代码也要使用 await。如果忘了加 await,你得到的就会是 promise 本身 Promise { <pending> },而不是它的值。

challenge icon

挑战

简单

这一步会打开课程中准备好的命令行笔记工具项目,而不是你在 Promises 中的项目文件(包括 count.js)。你之前的工作仍在它自己的课中。编辑 store.js 之前,请先阅读提供的文件。这份副本包含基于 promise 的笔记读取器及其错误处理。

store.js 现在使用 promise 版本的 readFile 和 writeFile,它的方法也都是 async。但目前还没有任何地方等待这些 promise 完成,所以每个命令都会出错。在 promise 必须完成后才能进行下一步的地方添加 await。

  1. 在 store.js 的 load 中,等待 readFile 完成后,再让 JSON.parse 使用文本。
  2. 在 store.js 中,load 是 async,因此它会返回一个 promise。在 store 设置 notes 的地方等待它。模块可以在顶层,也就是任何函数之外使用 await。
  3. 在 store.js 中,在 save 里等待 writeFile,并在 add 和 remove 中等待 this.save()。
  4. 在 notes.js 中,store.add 和 store.remove 现在都是 async。在 add 和 done 分支中等待它们,这样 n 和 removed 保存的就是值,而不是 promise。

这样总共要添加 7 个 await。之后每个命令都会像以前一样正常工作。list 会显示 notes.json 中的笔记。初始的两条笔记还在时,add Buy bread 会打印 Saved note 3.,接着运行 done 1 会打印 Done: Buy milk。

试试看:先不要做任何更改,在 $ node notes.js 后面的输入框中输入 list。你会看到 notes.json is damaged: ...,但文件本身没有问题:传给 JSON.parse 的是一个 promise,而不是文件文本。完成更改后,list 会再次显示笔记。然后输入 add Buy bread,看看是否显示 Saved note 3.。再输入 done 1,看看是否显示 Done: Buy milk。

亲手试试

import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';

const file = 'notes.json';

// readFile 和 writeFile 现在会返回 Promise。用 await 等待它们各自完成。
const load = async () => {
    if (!existsSync(file)) {
        return [];
    }
    try {
        return JSON.parse(readFile(file, 'utf8'));
    } catch (err) {
        console.error(`notes.json is damaged: ${err.message}`);
        process.exit(1);
    }
};

const store = {
    notes: load(),
    async save() {
        writeFile(file, JSON.stringify(this.notes, null, 2));
    },
    async add(text) {
        this.notes.push(text);
        this.save();
        return this.notes.length;
    },
    async remove(n) {
        const [text] = this.notes.splice(n - 1, 1);
        this.save();
        return text;
    },
};

export default store;
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

Node.js 基础中的所有课

自己练习: Express 在线编辑器