Menu
CoddyTech

Async và await

Thuộc phần Kiến thức cơ bản về Node.js trong hành trình Node.js của Coddy. Bài học 23 trên 33.

Một chuỗi lời gọi .then diễn đạt các bước khác với cách bạn hình dung: đọc, rồi phân tích, rồi lưu.

Bên trong một hàm async, await tạm dừng cho đến khi một promise hoàn tất và trả về giá trị của nó. Nếu promise thất bại, lỗi sẽ được ném tại đó, vì vậy try và catch sẽ xử lý lỗi. Trong một module, bạn cũng có thể dùng await ở cấp cao nhất.

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);

Hàm async luôn trả về một promise, vì vậy bên gọi hàm cũng phải chờ nó bằng await. Nếu quên await, bạn sẽ nhận được chính promise đó, Promise { <pending> }, thay vì giá trị của nó.

challenge icon

Thử thách

Dễ

Bước này mở dự án công cụ dòng lệnh ghi chú đã chuẩn bị sẵn của khóa học, chứ không phải các tệp dự án của bạn trong Promises (bao gồm cả count.js). Phần bạn làm trước đó vẫn nằm trong bài học riêng. Hãy đọc các tệp được cung cấp trước khi chỉnh sửa store.js. Bản sao này có trình đọc ghi chú dùng promise và phần xử lý lỗi.

store.js hiện dùng các phiên bản promise của readFile và writeFile, còn các phương thức của nó là async. Nhưng chưa có gì chờ các promise hoàn tất, nên mọi lệnh đều lỗi. Hãy thêm await ở những nơi cần đợi một promise hoàn tất trước bước tiếp theo.

  1. Trong store.js, ở load, hãy đợi readFile hoàn tất trước khi JSON.parse dùng văn bản.
  2. Trong store.js, load là async, nên nó trả về một promise. Hãy đợi promise đó ở chỗ kho lưu trữ gán giá trị cho notes. Một module có thể dùng await ở cấp cao nhất, bên ngoài mọi hàm.
  3. Trong store.js, hãy đợi writeFile trong save, và đợi this.save() trong add và remove.
  4. Trong notes.js, store.add và store.remove hiện là async. Hãy đợi chúng trong các nhánh add và done, để n và removed chứa giá trị chứ không phải promise.

Tổng cộng cần bảy await. Sau đó, mọi lệnh sẽ hoạt động như trước. list hiển thị các ghi chú trong notes.json. Với hai ghi chú ban đầu, add Buy bread in ra Saved note 3., rồi done 1 in ra Done: Buy milk.

Thử nhé: nhập list vào ô sau $ node notes.js trước khi thay đổi gì. Bạn sẽ thấy notes.json is damaged: ..., nhưng tệp vẫn ổn: JSON.parse nhận một promise thay vì văn bản trong tệp. Sau khi chỉnh sửa, list sẽ hiển thị lại các ghi chú. Tiếp theo, nhập add Buy bread và xem Saved note 3.. Nhập done 1 và xem Done: Buy milk.

Tự làm thử

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

const file = 'notes.json';

// readFile và writeFile giờ trả về promise. Dùng await để chờ từng promise.
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Kiến thức cơ bản về Node.js

Tự luyện tập: Playground Express