Kodokon kodokon.com

非同期を実践する

ルートでasync/awaitを使い、サーバーを落とさずに非同期のエラーを処理しましょう。

10 分 · 3 問

このレッスンを Kodokon で開く

本物のルートは、メモリ上の配列を返したりはしません。ファイルを読み、データベースに問い合わせ、他のAPIを呼び出します。これらの操作はすべて非同期です。async/awaitはJavaScript講座から知っていますね。サーバーの中できれいに使う方法を見ていきましょう。まず良い知らせです。ルート関数はasyncにできます。

JAVASCRIPT
import express from "express";
import { readFile } from "node:fs/promises";

const app = express();

app.get("/report", async (req, res) => {
  const raw = await readFile("report.json", "utf8");
  res.json(JSON.parse(raw));
});

app.listen(3000);

このコードは動きます……report.jsonが存在する限りは。読み取りに失敗すると、プロミスは拒否され、Expressのバージョンによっては、リクエストが応答のないままになったり、エラーがログに漏れ出したりします。プロのルールはこうです。すべての非同期ルートは自分のエラーを処理するtry/catchnext(error)を使って、です。

JAVASCRIPT
app.get("/report", async (req, res, next) => {
  try {
    const raw = await readFile("report.json", "utf8");
    res.json(JSON.parse(raw));
  } catch (error) {
    next(error);
  }
});

next(error)は、エラーを一元化されたエラーハンドラーへ渡します。これは四つのパラメーターを持つミドルウェアで、すべてのルートより後ろに宣言します。エラーを記録し整形する場所が一つだけあること。それこそが、保守できるAPIを週末の趣味プロジェクトと分けるものです。

JAVASCRIPT
app.use((err, req, res, next) => {
  console.error(err);
  res.status(500).json({
    error: "Internal server error"
  });
});
常に最後、ルートより後ろに

最後に身につけたい習慣は、並列処理です。二つのawaitで連鎖させた独立した二つの操作は、順番に一つずつ実行されます。Promise.allはそれらを同時に起動します。応答時間は、二つの合計ではなく、最も遅いものの時間になります。

JAVASCRIPT
async function fetchUsers() {
  return [{ id: 1, name: "Ada" }];
}

async function fetchTasks() {
  return [{ id: 1, title: "Review the code" }];
}

app.get("/dashboard", async (req, res, next) => {
  try {
    const [users, tasks] = await Promise.all([
      fetchUsers(),
      fetchTasks()
    ]);
    res.json({ users, tasks });
  } catch (error) {
    next(error);
  }
});

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. asyncルートで、catchが捕まえたエラーをどうしますか?
    • console.logで記録して先に進む
    • next(error)を呼び出してエラーハンドラーへ渡す
    • リクエストを自動的に再試行する
  2. 独立した二つの非同期呼び出しを、できるだけ速く実行しなければなりません。どの方法を選びますか?
    • 続けて二つのawaitで連鎖させる
    • Promise.allで同時に起動する
    • 二つの間にsetTimeoutを使う
  3. Expressはエラー処理ミドルウェアをどうやって見分けますか?
    • errを先頭にした四つのパラメーターのシグネチャによって
    • errorHandlerでなければならない関数名によって
    • ファイルの先頭にあるその位置によって