3. コールバック

Node.jsのコールバック関数の基本概念と使い方を学びます。適切なコールバックの書き方とエラーハンドリングの方法を実践的に学習します。

この章で学べること

  • コールバックとは何かを理解する
  • 非同期処理でコールバックがなぜ必要かを学ぶ
  • エラー・ファースト・コールバックのルールを理解する
  • 実践的な演習を通じてコールバックを習得する

コールバックとは?

コールバックとは「ある処理が終わった後に実行される関数」のことです。コールバックの実態はただの関数です。通常の関数との違いは、他の関数に渡され、適切なタイミングで呼び出されることを期待するところです。

Node.jsでは関数を引数として渡すことができ、この仕組みを使ってコールバックを実現しています。

以下は、コールバックのサンプルコードです。doSomethingはcallbackという引数を受け取ります。このcallbackは関数であり、callback() のようにカッコをつけて記述することで関数を実行することができます。

function doSomething(callback) {
  console.log("処理中...");
  callback(); // コールバックを実行
}

以下はdoSomethingを実行するサンプルコードです。doSomethingの引数は関数です。Node.jsではこのようなコードをよく目にすることになります。

doSomething(() => {
  console.log("完了しました!");
});

実行結果

処理中...
完了しました!

コールバックはなぜ必要?

Node.jsは ファイル読み込みや通信は非同期処理で処理します。非同期処理は後続のコードの実行をブロックしません。そのため、非同期処理が終わった後に実行したい処理を適切なタイミングで実行する仕組みが必要になります。非同期処理が終わったらこの処理を実行してねという仕組みを実現するのがコールバックです。

具体例:ファイル読み込み

ファイルを読み込んで、その内容を表示する処理を例に説明します。ファイル読み込みは時間がかかる処理なので、非同期で実行されます。

// ファイルを読み込む(非同期処理)
fs.readFile('data.txt', 'utf8', (err, data) => {
  if (err) {
    console.log('エラーが発生しました:', err);
  } else {
    console.log('ファイルの内容:', data);
  }
});

console.log('この行は先に実行されます');

実行結果

この行は先に実行されます ファイルの内容: Hello World!

この例では、fs.readFileの第3引数として渡されている(err, data) => { ... }がコールバック関数です。ファイルの読み込みが完了したタイミングで、このコールバック関数が実行されます。

コールバックの引数のルール

Node.jsでは「エラー・ファースト・コールバック(error-first callback)」というルールがあります。名前の通り、エラーをコールバックの最初の引数に設定するというものです。

function callback(err, result) {
  if (err) {
    // エラー処理
  } else {
    // 成功時の処理
  }
}

例えば、先ほどのファイル読み込みやデータベース操作の場合、ファイルの読み込みやデータベースの問い合わせに失敗する可能性があります。そのため、エラーの場合と成功時の場合とで処理を分ける必要があります。

コールバックのerr変数は、エラーが発生しない場合はnullが設定されます。

演習課題

演習①:処理の流れを考えてみよう

以下のコードを実行したときの出力順序を予想してください。

function greet(name, callback) {
  console.log("こんにちは、" + name + "さん");
  callback();
}

greet("太郎", () => {
  console.log("コールバック処理を実行!");
});

出力順序を選択してください

演習②:コールバック関数を識別しよう

以下のコードの中で、コールバック関数を識別してください。

問題1
function greet(name, callback) {
  console.log("こんにちは、" + name);
  callback();
}

greet("太郎", () => {
  console.log("よろしくお願いします");
});
問題2
function calculate(a, b) {
  return a + b;
}

const result = calculate(5, 3);
問題3
fs.readFile('file.txt', (err, data) => {
  if (err) {
    console.log("エラー:", err);
  } else {
    console.log("データ:", data);
  }
});
問題4
setTimeout(() => {
  console.log("1秒後に実行");
}, 1000);
問題5
// データベースからユーザー情報を取得
db.getUser(123, (err, user) => {
  if (err) {
    console.log('ユーザーが見つかりません:', err);
  } else {
    console.log('ユーザー名:', user.name);
    console.log('メールアドレス:', user.email);
  }
});

演習③:基本のコールバック呼び出し

doTask 関数を作成し、コールバックを使って以下の文字をconsole.logで表示してください。

タスクを実行中...
完了しました!
// ここにコードを書いてください
function doTask(callback) {
  // ここを完成させよう
}

doTask(() => {
  // ここを完成させよう
});

実践してみよう

上記のコードを実際に書いて実行してみましょう。コードエディタで練習できます。

コードエディタで練習する

演習④:コールバックのエラー処理

getData関数は、成功したらデータを返し、失敗したらエラーを返します。エラーが発生した時はエラーを表示、成功時はデータを表示してください。

function getData(callback) {
  const isError = Math.random() > 0.5; // ランダムでエラーが発生
  if (isError) {
    callback("エラーが発生しました", null);
  } else {
    callback(null, "データを取得しました");
  }
}

getData((err, data) => {
  // ここを完成させよう
});

実践してみよう

上記のコードを実際に書いて実行してみましょう。コードエディタで練習できます。

コードエディタで練習する

まとめ

コールバックは、Node.jsの非同期処理において重要な概念です。以下のポイントを理解しましょう。

  1. コールバックとは: 処理が終わった後に実行される関数
  2. 非同期処理での必要性: 処理の完了を待つための仕組み
  3. エラー・ファースト・コールバック: エラーを最初の引数に設定するルール
  4. 実践的な使い方: API呼び出しやファイル操作で頻繁に使用

コールバックは、Node.jsの非同期処理の基礎となる重要な概念です。慣れるまでは複雑に感じるかもしれませんが、実際にコードを書いて練習することで理解が深まります。

次回予告

次回はNode.jsのPromiseについて学びます。