自分自身が勘違いしていたので記事にします。
問題
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
</head>
<body>
<script>
console.log(`Start ${new Date()}`)
// 3秒後にCallbackと表示
setTimeout(() => {
console.log(`Callback ${new Date()}`)
}, 3000)
// 10秒間かかる処理を実行する
const now = new Date()
let dummy = 0
while (new Date() - now < 10000) {
dummy++ // 適当な計算処理
}
console.log(`End ${new Date()}`)
</script>
</body>
</html>
このHTMLをブラウザで表示させると、コンソールにはStart
, Callback
, End
がどの順番で表示されるでしょうか?
-
Start
が表示される - 3秒後に
Callback
と表示されることが予約される - 10秒間かかる処理を実行する
この間に少なくとも3秒間経過するのでCallback
と表示される -
End
と表示される
よってStart
→Callback
→End
だと思っていませんか?
残念!不正解です。
実行結果
Start Tue May 21 2019 10:39:40 GMT+0900 (日本標準時)
End Tue May 21 2019 10:39:50 GMT+0900 (日本標準時)
Callback Tue May 21 2019 10:39:50 GMT+0900 (日本標準時)
実際にはStart
→End
→Callback
の順番で実行されます。
ポイント
ここら辺がポイントです。
-
setTimeout
は規定時間後に関数の実行を予約する - しかし、JavaScriptはシングルスレッドで実行される
- つまり、2つ以上の処理を並行して実行できない
- 2つ以上の関数を同時実行できない
解説
つまり、正しくは以下のような順序になります。
-
Start
が表示される - 3秒後に
Callback
と表示されることが予約される - 10秒間かかる処理を実行する
この間に少なくとも3秒間経過するが、まだメインの処理が実行されているので2.の関数を実行開始できない -
End
と表示される - メインの処理が完了して次の処理を実行可能になったので、2.の関数が実行される
Callback
と表示される
よってStart
→End
→Callback
の順番で実行されます。
ちなみに、setTimeout
を使う場合以外でも、
- Ajaxでの非同期処理
- Node.jsでのファイル読み込みなどの非同期処理
などの場合もシングルスレッドなので同様の処理順になります。