はじめに
JSにおける同期処理と非同期処理の違いについて知らなかったので、理解を図るべくメモを残したいと思います。
この記事では、まだあまり深いところにはまだ立ち入らないと思います。
コードなどは以下のサイトからお借りしています。
同期処理(sync)とは?
- (おそらく)初心者が最初に学ぶコードの書き方
- コードを順番に処理していき、一つの処理が終了するまで次の処理は始まらない
- つまり実行している処理は一つだけ
非同期処理(async)とは?
- コードは順番に処理して行くが、一つの非同期処理が終わるのを待たずに次の処理を評価する
- つまり同時に実行している処理が複数ある
なぜ非同期処理が必要なのか?
- 同期処理だと問題になる場面がある
- 例えば以下のように同期的にブロックする処理がある場合
次のコードのblockTime関数は指定したtimeoutミリ秒だけ無限ループを実行し、同期的にブロックする処理です。 timeoutミリ秒経過したかの判定には、無限ループの中でUnix時間(1970年1月1日午前0時0分0秒から経過した時間)のミリ秒を返すDate.nowメソッドを利用しています。 このblockTime関数を呼び出すと、指定時間が経過するまで次の処理(タスクB)は呼ばれません。
function taskA() {
console.log("タスクAを実行 at " + Date.now());
}
function taskB() {
console.log("タスクBを実行 at " + Date.now());
}
// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
// Date.now()は現在の時間をUnix時間(1970年1月1日午前0時0分0秒から経過した時間)のミリ秒を返す
const startTime = Date.now();
// `timeout`ミリ秒経過するまで無限ループをする
while (true) {
const diffTime = Date.now() - startTime;
if (diffTime >= timeout) {
return; // 指定時間経過したら関数の実行を終了
}
}
}
taskA();
blockTime(1000); // 他の処理を1000ミリ秒(1秒間)ブロックする
taskB();
- 同期的にブロックする処理があると、ブラウザでは大きな問題になる
- なぜならJSはブラウザのメインスレッドで基本的に処理されるから
- メインスレッドでは表示の更新といったUIの処理も行なっている
- そのためメインスレッドをJSの処理で占有すると、表示の更新もフリーズしたようになる
先程のコード例では1秒間コードの実行をブロックしています。
これにより、画面も1秒間スクロールできないといったUI面での問題が出るわけですね。
しかし・・・非同期処理もメインスレッドで処理される
同期処理はメインスレッドで基本的に処理され、ブロックする処理などがあると、他の表示更新などに悪影響を及ぼします。
しかし非同期処理も基本的にはメインスレッドで処理されます。
非同期処理はメインスレッドで処理されないから、同期的なブロックの影響を受けないと思いがちですが、そうではありません。
function taskA() {
console.log("タスクAを実行 at " + Date.now());
}
function taskB() {
console.log("タスクBを実行 at " + Date.now());
}
function taskAsync() {
console.log("非同期のタスクを実行 at " + Date.now());
}
// 指定した`timeout`ミリ秒経過するまで同期的にブロックする関数
function blockTime(timeout) {
const startTime = Date.now();
while (true) {
const diffTime = Date.now() - startTime;
if (diffTime >= timeout) {
return; // 指定時間経過したら関数の実行を終了
}
}
}
const startTime = Date.now();
taskA();
// 10ミリ秒後にコールバック関数を呼び出すようにタイマーに登録する
setTimeout(() => {
const endTime = Date.now();
taskAsync();
console.log(`非同期処理のコールバックが呼ばれるまで${endTime - startTime}ミリ秒かかりました`);
}, 10);
blockTime(1000); // 1秒間処理をブロックする
taskB();
上のコードでは、setTimeout関数で、非同期処理のコールバック(taskAsync())が呼ばれるまで何秒かかったかを計測しています。
setTimeout関数内では10ミリ秒後にtaskAsync()を呼び出すように設定しています。
しかし実行してみると、コールバックが呼び出されるまでに1秒以上を要します。
これは非同期処理が同期処理と同じくメインスレッド内で処理されており、同期的なブロック(blockTime(1000))の影響を受けているためです。
疑問など
- 「同期的に」ブロックする処理とは何か?非同期的にブロックする処理があるのか?
- 非同期処理もメインスレッドで処理されるとしたら、非同期処理の存在価値は?
参考・引用