悩みの種であるJavaScriptの非同期処理について、感覚を掴めたような気がするので備忘録です。Qiitaの記事が非常にわかりやすかったので参照します。
JavaScriptの同期、非同期、コールバック、プロミス辺りを整理してみる - Qiita
JavaScriptの実行については次のようなことが言えるようです。
排便で例えてみます。うんちする、トイレを流す、パンツを履くの3つの動作を実行する関数で表しています。ただし、うんちをするのは非常に時間のかかる処理なので、パフォーマンス重視のために非同期関数となっています。(setTimeout
を使って非同期処理にしています。)
// unchi.js
const fireUnchi = () => {
setTimeout(() => {
console.log('うんちしました。');
}, 0)
}
const flushToilet = () => {
console.log('トイレを流しました。')
}
const wearPants = () => {
console.log('パンツを履きました。')
}
fireUnchi();
flushToilet();
wearPants();
実行すると次のようになります。
$ node unchi.js
トイレを流しました。
パンツを履きました。
うんちしました。
悲劇です。setTimeout
は0秒なので即座に実行されても良さそうですが、この挙動は同期処理と非同期処理実行キューへの登録する方法を掴むと理解しやすいです。
同期処理では3つの関数を順番に実行していますが、最初のfireUnchi
関数が実行されたときはsetTimeout
のタイマーが登録されています。setTimeout
の第二引数には0
が指定されているので0秒=即座に実行キューに登録されそうですが、同期的な処理ではタイマー登録だけです。flushToilet
、wearPants
が実行キューに登録されたのでlogが表示され、同期的な処理が終了した段階で次に非同期処理が始まります。タイマー指定は0秒なので非同期処理に移ってから0秒後にfireUnchi
は実行キューに登録されます。
つまり、0秒後だろうが1分後だろうが、\b同期的な処理が終了してからタイマーのカウントがスタートするので同期処理より前になることはないということですね。
非同期処理を同期的に扱う方法はcallback関数やES6から追加されたPromiseを使う方法などがありますが、それはまた別に書こうと思います。