6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2022年1月31日 JavaScript (JS Primer) 非同期処理:コールバック

多くのプログラミング言語にはコードの評価の仕方として、同期処理(sync)と非同期処理(async)という大きな分類があります。 非同期処理はJavaScriptにおけるとても重要な概念です。

まずは非同期処理での例外処理、非同期処理の違いから学んでいきたいと思います。

同期処理

今まで書いていたコードは同期処理と呼ばれているものです。 同期処理ではコードを順番に処理していき、ひとつの処理が終わるまで次の処理は行いません。

メモ

  • コードの処理は順番に行われる。
  • ひとつの処理が終わるまで次の処理は行わない。
  • 実行している処理は常にひとつである。

とても直感的ではありますが、その一方で同期的にブロック処理が行われている場合に問題があります。
同期処理ではひとつの処理が終わるまで、次の処理へ進むことができません。

timeoutミリ秒だけ無限ループを実行し、同期的にブロックする"blockTime関数"を呼び出すと、指定時間が経過するまで次の処理(次の行)は呼ばれません。

// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
    const startTime = Date.now();
    // `timeout`ミリ秒経過するまで無限ループをする
    while (true) {
        const diffTime = Date.now() - startTime;
        if (diffTime >= timeout) {
            return; // 指定時間経過したら関数の実行を終了
        }
    }
}
console.log("処理を開始");
blockTime(1000); // 他の処理を1000ミリ秒(1秒間)ブロックする
console.log("この行が呼ばれるまで処理が1秒間ブロックされる");
  • startTimeは、代入した時刻を固定してdiffTime内で呼び出すために代入しています。
  • while文はif文がtrueになるまでループ処理がされます。
  • diffTimeはDate.nowという時間が進むにつれて変化する値からstartTimeを引いた値を代入しています。
  • 引数であるtimeout(1000ミリ秒)よりdifftimeの値が大きくなったタイミングでwhile文のループ処理が終了してreturnされます。

Date.now() メソッドは、UTC (協定世界時) での 1970 年 1 月 1 日 0 時 0 分 0 秒 から現在までの経過時間をミリ秒単位で返します。

非同期処理

非同期処理はコードを順番に処理していきますが、ひとつの非同期処理が終わるのを待たずに次の処理を評価します。 つまり、非同期処理では同時に実行している処理が複数あります。

メモ

  • コードを順番に処理を行うが、ひとつの非同期処理が終わるのを待たずに次の処理を実行する。
  • 同時進行である。

非同期処理の代表的な例としてsetTimeout関数が挙げられます。

// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
    const startTime = Date.now();
    while (true) {
        const diffTime = Date.now() - startTime;
        if (diffTime >= timeout) {
            return; // 指定時間経過したら関数の実行を終了
        }
    }
}

console.log("1. setTimeoutのコールバック関数を10ミリ秒後に実行します");
setTimeout(() => {
    console.log("3. ブロックする処理を開始します");
    blockTime(1000); // 他の処理を1秒間ブロックする
    console.log("4. ブロックする処理が完了しました");
}, 10);
// ブロックする処理は非同期なタイミングで呼び出されるので、次の行が先に実行される
console.log("2. 同期的な処理を実行します");
実行

実行結果の流れ
1.setTimeoutのコールバック関数を10ミリ秒後に実行します
2.同期的な処理を実行します
3.ブロックする処理を開始します
4.ブロックする処理が完了しました

つまりsetTimeoutの第2引数である10ミリ秒の間にも同期処理は動いていて、その間に"2.同期的な処理を実行します"が行われます。そして10ミリ秒が経過した後に、setTimeoutのコールバック関数が実行されているという流れになります。

このように、非同期処理(setTimeoutのコールバック関数)は、コードの見た目上の並びとは異なる順番で実行されることがわかります。

参考

JS primer 非同期処理:コールバック

Date.now()