Menu
CoddyTech

asyncとawait

CoddyのNode.jsジャーニー「Node.jsの基礎」セクションの一部。レッスン 23/33。

.thenをつなげて呼び出す書き方は、手順を考えるときの「読み込み、次に解析、次に保存」という順序とは異なる読み方になります。

async関数の中では、awaitはプロミスが確定するまで処理を一時停止し、その値を返します。失敗したプロミスはそこで例外をスローするため、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関数は必ずプロミスを返すため、呼び出し元でもそれをawaitします。awaitを忘れると、値ではなくプロミスそのものPromise { <pending> }を受け取ります。

challenge icon

チャレンジ

簡単

このステップでは、Promisesのプロジェクトファイル(count.jsを含む)ではなく、コース用に用意されたメモのコマンドラインツールのプロジェクトを開きます。前のレッスンでの作業は、そのレッスン内に残っています。store.jsを編集する前に、用意されたファイルを読んでください。このコピーには、Promiseベースのメモ読み込み処理とエラー処理が含まれています。

store.jsでは、Promise版のreadFileとwriteFileを使うようになり、メソッドはasyncになっています。しかし、まだPromiseの完了を待つ処理がないため、すべてのコマンドが失敗します。次のステップに進む前にPromiseの完了が必要な箇所に、awaitを追加してください。

  1. store.jsのloadで、JSON.parseがテキストを使う前に、readFileの完了を待ちます。
  2. store.jsでは、loadはasyncなので、Promiseを返します。ストアが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のメモを表示します。最初の2つのメモがある状態で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プレイグラウンド