JavaScriptのコールバック関数

プログラミング

コールバック関数とは

「関数の参照を引数として他の関数に渡し、
渡された側の関数の中で、その関数を必要なタイミングで呼び出す仕組み」

コールバック関数が必要な理由

次のような「いつ実行するかわからない」処理があります。

状況実行タイミング
clickユーザー次第
通信完了サーバ次第
setTimeout指定時間後
Promise非同期完了後


イベント発生時や非同期完了時に実行する処理を、外部から柔軟に差し替え可能にするための仕組みが必要で、それを可能にしたのがコールバック関数です。

例えば
button.addEventListener関数に関数の参照を引数(コールバック関数)として渡すことで、ボタンにイベントが発生したときにコールバック関数で記述した処理を実行することができます。

また、引き渡すコールバック関数の参照を変更することで、ボタンイベントが発生したときの処理にバリエーションを持たせることができることができます。

① 「イベント発生時に実行する処理」を後から渡せる

では、

callback

を登録しています。

つまり:
「click が発生したら、この関数( callback関数)を実行してください」
という“処理内容”を外から渡しているわけです。

なぜ必要なのか
addEventListener 自体は、「イベントを監視する仕組み」しか持っていません。
でもクリック時に何をするかは場面によって違います。

例えば:

  • 色を変える
  • メッセージ表示
  • サーバ通信
  • モーダルを開く
  • 音を鳴らす

など様々です。

なので:

「実際の処理」は外から渡せるようにする必要があります。

これがコールバックです。

② 処理内容を自由に差し替えられる

「コールバック関数の参照を変更することで、ボタンイベント時の処理にバリエーションを持たせられる」

例えば:

button.addEventListener(“click”, showMessage);

にもできるし、

button.addEventListener(“click”, sendData);

にもできます。

つまりイベント監視の仕組みは同じで、実行処理だけ差し替え可能になります。

これが非常に重要です。

addEventListener は「汎用化」されている

もしコールバックが無かったら、

button.addEventListenerAndShowMessage()

button.addEventListenerAndSendData()

button.addEventListenerAndPlaySound()

のように、処理ごとに別関数が必要になります。

でもコールバックなら:

button.addEventListener(“click”, 任意の関数);

だけで済みます。

つまり「イベント監視」と「実際の処理」を分離できるのです。

コールバック関数の構成(基本形)

次の3つで構成するのが基本形です。

① コールバック関数を定義する

 「あとで実行したい処理」を定義しています。

② コールバックを受け取る関数

 これは、「渡された関数を実行する仕組み」です。

③ コールバック関数を渡す

 これは、「この関数を使ってください」とコールバック関数の参照を渡しています。

コールバック関数の構成(コールバック関数を無名関数で記述)

① コールバック関数を無名関数として引数内に定義する

 「あとで実行したい処理」を定義しています。

② コールバックを受け取る関数

 これは、「渡された関数を実行する仕組み」です。

③ コールバック関数を渡す

 これは、「この関数を使ってください」と無名関数で記述したコールバック関数の参照を渡しています。

コールバック関数の構成(アロー関数を無名関数で記述)

① コールバック関数をアロー関数として引数内に定義する

 「あとで実行したい処理」を定義しています。

② コールバックを受け取る関数

 これは、「渡された関数を実行する仕組み」です。

③ コールバック関数を渡す

 これは、「この関数を使ってください」とアロー関数で記述したコールバック関数の参照を渡しています。

コールバック関数の例(クリックイベント)

addEventListenerでブラウザ内に「button が click されたら callback を実行」という情報を登録する際、 myCallbackの参照が登録される。

 そして、クリックイベントが発生したとき、登録されたコールバック関数が実行される。

この登録とコールバック関数を呼び出す実態は、ブラウザにある機能(プログラム)。

動作の概要説明

① addEventListenerで登録

button.addEventListener(“click”, myCallback);を実行すると、ブラウザ内部ではイメージ的に「button に click イベントが発生したらmyCallback を呼び出す」という情報を保存します。

このとき保存されるのは
myCallback
つまり:コールバック関数の参照です。

実行結果ではありません。

ブラウザ内部イメージ

概念的にはブラウザ内部で:

eventMap = {
    button: {
        click: [myCallback]
    }
}

のような情報を保持しているイメージです。
※ 実際の実装はブラウザごとに違います。

② ブラウザがイベント監視

ブラウザは常に:

マウス操作
キーボード
スクロール
タッチ
ネットワーク
タイマー

などを監視しています。

つまりbutton がクリックされたか?を内部で見ています。

③ click発生

ユーザーがクリックすると、ブラウザ内部イベントシステムが「button の click 発生」を検知します。

④ 登録済みcallbackを実行

そして内部的に概念として:

myCallback();

を呼び出します。

より正確には:

myCallback(event);

に近いです。

コールバック関数の例(非同期I/O処理)

①書き込み 完了後に実行したい関数(コールバック関数)を定義

function onWriteComplete(err) {
    if (err) {
        console.log(“ファイルの書き込みに失敗しました。”);
    } else {
        console.log(“ファイルの書き込みが正常に完了しました!”);
    }
}

これは「書き込み完了後に実行したい処理」です。

つまりコールバック関数です。

② コールバックを渡す

writeFileAsync(“output.txt”, “こんにちは、非同期ファイル書き込み!”, onWriteComplete);

と呼ぶと、

callback = onWriteComplete

になります。

つまり「処理完了したら onWriteComplete を呼んでください」という依頼です。

③ writeFileAsync の役割

function writeFileAsync(filename, data, callback)

これは「コールバックを受け取る側」です。

さらに内部で別のコールバックを使っています。

ここが重要です。

fs.writeFile(filename, data, “utf8”, (err) => {

この:

(err) => { … }

もコールバック関数です。

fs.writeFile の仕組み

Node.js内部では:

ファイル書き込み完了したらこの関数を呼ぶという登録をしています。

つまり:

(err) => { … }

を callback として渡している。

コールバックが二重になっている

実はこのコードには:

コールバック誰が呼ぶ?
(err)=>{}Node.js内部
onWriteCompletewriteFileAsync

があります。

流れを図にすると

呼び出し側

writeFileAsync(…, onWriteComplete);

writeFileAsync 内部

fs.writeFile(…, (err)=>{ … });

Node.js が書き込み開始

(非同期)

書き込み完了

Node.js内部が:

(err)=>{ … }

を実行

writeFileAsync がcallback 実行

callback(null);

または:

callback(err);

実際には

onWriteComplete(err);

が呼ばれる

callback(err) の意味

callback(err);

onWriteComplete(err);

を意味します。

callback(null) の意味

callback(null);

onWriteComplete(null);

です。

つまりエラー無しを表します。

Node.jsの伝統的ルール

Node.js のコールバックはよく

callback(error, result)

形式です。

今回なら:

callback(err)

だけですが、基本思想は同じです。

このコードの本質

「非同期処理が終わったあとに実行する処理を、

関数として先に渡しておく」

ことです。

つまり

未来に実行する処理

をコールバック関数で渡しています。

①後で実行する関数、②関数を受け取って後で実行する仕組み、③関数を引数として渡すに対応させると

役割このコード
① 後で実行する関数onWriteComplete
② 関数を受け取って後で実行する仕組みwriteFileAsync
③ 関数を引数として渡すwriteFileAsync(…, onWriteComplete)

です。

さらに内部では

内部コールバック
(err)=>{} を fs.writeFile に渡している

という二段階構造になっています。

Fetchを利用した非同期Webアプリ その3でコールバック関数についてまとめましたが、図をいれて、もう少し分かりやすい方にまとめ直しました。

~~~~~~~~~~~~~~~~~~~~~~~~~

プログラミングをやりたいと思うのですが、なかなか時間がとれません。
PHPで作成したWEB版のレシート管理プログラムはパソコン版としては完成しているのですが、スマホ対応(レシポンシブ)が途中で止まっています。

優先度は

  1. 介護
  2. 仕事
  3. ブログ
  4. 父の住んでいた家の整理
  5. 菜園や庭の整備
  6. YouTube
  7. プログラミング

その他、雑多なものが割り込んできます。(町内会班長の役割など)

しばらくの間は、レシポンシブ対応は私の頭の中で温めておくことにしましょう。

タイトルとURLをコピーしました