JavaScript アドバンス

JavaScript コールバック

「後でかけ直す(Call back)ね!」

JavaScriptのコールバック(Callback)とは、別の関数に引数(Argument)として渡され、特定のタスクを完了するために後で実行(「呼び出し」)される関数のことです。

このメカニズムは、JavaScriptのイベント駆動型(Event-driven)および非同期(Asynchronous)プログラミングモデルにおいて非常に重要な役割を果たしています。

1. コールバック関数とは何か

  • コールバック関数は、別の関数に引数として渡される関数です。
  • コールバック関数は、「後で実行」されることを目的としています。
  • 「後で」とは、通常、特定のイベントが発生したときや、非同期操作が完了したときを指します。

「コールバック」という名前は、外側の関数が自身のタスクを終えた後に、あなたを「呼び出し直す(Call you back)」という概念に由来しています。

2. コールバックの種類

2.1 非同期コールバック (Asynchronous Callbacks)

非同期コールバックは後で実行されるため、メインプログラムは待機することなく実行を継続できます。これは、ネットワークリクエストのような時間のかかるタスク中にアプリケーションがフリーズするのを防ぐために不可欠です。

2.2 同期コールバック (Synchronous Callbacks)

同期コールバックは、外側の関数内で即座に実行され、完了するまで後続の操作をブロックします。map()filter()forEach() などの配列メソッドは同期コールバックを使用します。

3. イベントハンドリング (Event Handling)

コールバックはJavaScript、特にイベントハンドリング(Event Handling)において頻繁に使用されます。
ボタンのクリックやキー入力などのユーザーインタラクションは、イベントリスナー(Event Listener)にコールバック関数を渡すことで処理できます。

3.1 イベントリスナーの例

// "myButton"というIDの要素を取得し、クリックイベントにコールバックを登録
document.getElementById("myButton").addEventListener("click", displayDate);

上記の例では、displayDateaddEventListener() メソッドに引数として渡されたコールバック関数です。ユーザーがID "myButton" のボタンをクリックしたときに displayDate が呼び出されます。

注意: 関数を引数として渡す際は、括弧 () を付けないようにしてください。

  • 正しい例: displayDate
  • 間違い: displayDate()

4. 非同期操作

setTimeout() のようなウィンドウ関数は、指定された遅延の後でコードを実行するためにコールバックを使用します。

4.1 setTimeout の例

// 3000ミリ秒(3秒)後に myFunction を実行
setTimeout(myFunction, 3000);

function myFunction() {
  document.getElementById("demo").innerHTML = "大好きです!!";
}

上記の例では、myFunctionsetTimeout() に引数として渡されたコールバック関数です。3000 は、myFunction が呼び出されるまでのミリ秒数です。

5. 配列メソッド (Array Methods)

map()filter()forEach() といった多くの組み込み配列メソッドは、各要素に対して実行するアクションを定義するためにコールバック関数を受け取ります。

5.1 forEach() の例

forEach() メソッドは、配列の各要素に対して一度ずつコールバック関数を呼び出します。

const numbers = [45, 4, 9, 16, 25];
let txt = "";
// 各要素に対して myFunction を実行
numbers.forEach(myFunction);

function myFunction(value) {
  txt += value + "<br>";
}

5.2 map() の例

map() メソッドは、各配列要素に対して関数を実行し、その結果から新しい配列を作成します。

const numbers1 = [45, 4, 9, 16, 25];
// numbers1 の各要素を2倍にした新しい配列を作成
const numbers2 = numbers1.map(myFunction);

function myFunction(value) {
  return value * 2;
}

6. 実行シーケンスの制御 (Sequence Control)

関数の実行タイミングをより精密に制御したい場合があります。
例えば、計算を行ってからその結果を表示したいケースを考えてみましょう。

6.1 パターンA:個別に呼び出す

計算関数 myCalculator を呼び出し、その後に表示関数 myDisplayer を呼び出すことができます。

// 何かを表示する関数
function myDisplayer(some) {
  document.getElementById("demo").innerHTML = some;
}

// 合計を計算する関数
function myCalculator(num1, num2) {
  let sum = num1 + num2;
  return sum;
}

// 計算を実行
let result = myCalculator(5, 5);

// 結果を表示
myDisplayer(result);

6.2 パターンB:関数内で関数を呼ぶ

あるいは、myCalculator 内から myDisplayer を呼び出すこともできます。

// 何かを表示する関数
function myDisplayer(some) {
  document.getElementById("demo").innerHTML = some;
}

// 合計を計算し、表示関数を直接呼ぶ
function myCalculator(num1, num2) {
  let sum = num1 + num2;
  myDisplayer(sum);
}

// 計算を実行
myCalculator(5, 5);

6.3 各パターンの問題点

  • パターンAの問題: 結果を表示するために2つの関数を呼び出す必要があります。
  • パターンBの問題: 計算関数が表示処理を強制するため、表示を止めることができません。

6.4 パターンC:コールバックの導入

ここでコールバックの出番です。myCalculator にコールバック(myCallback)を渡し、計算が終わった後にそのコールバックを実行させるようにします。

function myDisplayer(some) {
  document.getElementById("demo").innerHTML = some;
}

// 第3引数にコールバック関数を受け取る
function myCalculator(num1, num2, myCallback) {
  let sum = num1 + num2;
  myCallback(sum);
}

// myDisplayer をコールバックとして渡す
myCalculator(5, 5, myDisplayer);

この方法なら、myCalculator を呼び出す際に「結果をどう処理するか(表示するのか、ログに出すのか等)」を柔軟に決めることができます。

7. コールバックの主要コンセプト

7.1 引数としての関数 (Function as an Argument)

JavaScriptでは、関数は他のバリアブル(変数)やオブジェクトと同様に扱うことができるため、引数として他の関数に渡すことが可能です。

7.2 実行の遅延 (Deferred Execution)

コールバックの最大の利点は、実行の遅延が可能になることです。つまり、コールバック関数は即座に実行されるのではなく、特定の条件が満たされたとき、イベントが発生したとき、あるいは非同期操作が完了したときに実行されます。

このメカニズムにより、サーバーからのデータ取得、ファイルの読み込み、ユーザーのクリック待機といった「時間のかかるタスク」を待っている間も、プログラムは他のコードを実行し続けることができるのです。