🍋

【JavaScript】即時実行と遅延実行の違い

に公開

コールバック関数を渡すべき処理に関数呼び出しを渡してしまい意図した動作にならない。開発中によく間違えたので、備忘録代わりにまとめます。

TL;DR

  • 「関数を渡す」= 遅延実行、「関数を呼び出して結果を渡す」= 即時実行という違い
  • 関数そのものを渡すか関数の呼び出し結果を渡すかで実行されるタイミングが異なる
  • 必要なときに関数を実行するなら関数を渡す。今すぐ実行したいなら関数をコールする

具体例

Promise の処理が完了した後にログを出す実装を考えます。
まずは間違った実装から。

somePromise().then(console.log("done"));
// "done" がその場で出力されるだけで、then() には undefined が渡る

次の順序で処理されます。

  1. somePromise() が実行される
  2. console.log("done") が実行される
  3. .then() で Promise に成功時に実行する処理(2. のundefined)を登録する
  4. Promise が解決されても何も行われない

意図した結果にするには次のように実装します。

somePromise().then(() => console.log("done"));
// Promise の処理が成功した場合に "done" が出力される

実行順序は次の通りです。

  1. somePromise() が実行されてPromise オブジェクトが返る
  2. .then() で Promise に「成功時に実行する関数」を登録する
  3. Promise が解決されたタイミングで関数 () => console.log("done") が実行される。done が出力される。

間違った実装では console.log() がすぐに呼ばれたのに対し、正しい実装では console.log() が後に実行されました。前者を即時実行、後者を遅延実行と表現します。

まとめ

間違った実装 : console.log() 正しい実装 : () => console.log()
渡しているもの console.log() の実行結果(undefined) 無名関数(関数そのもの)
実行タイミング その場で即時実行 後で呼ばれるときに実行

この差は単純な構文の違いに見えても、プログラム全体の挙動や副作用の発生タイミングに大きく影響します。
特に副作用(ログ出力、DOM操作、APIコールなど)を伴う処理では注意が必要です。

遅延実行のユースケース

具体例で示したもの以外にいくつか遅延実行のユースケースと即時実行時の結果を示します。

setTimeout

// 遅延実行(1秒後にログ)
setTimeout(() => console.log("Hello"), 1000);

// 即時実行(間違い)
setTimeout(console.log("Hello"), 1000);
// "Hello" がすぐ表示され、1秒後には何も起きない

イベントリスナー

// 遅延実行(クリック時にログ)
button.addEventListener('click', () => console.log("clicked"));

// 即時実行(間違い)
button.addEventListener('click', console.log("clicked"));
// ページ読み込み時に "clicked" が出力されるだけ

Array.map

// 遅延実行(要素ごとに2倍になる)[2,4,6]
const doubled = [1,2,3].map(n => n * 2);

// 即時実行
const doubled = [1,2,3].map(console.log(1));
// "1" がすぐ出力され、map には undefined が渡るので壊れる

Discussion