例

var r = new Promise(function(resolve, reject){
console.log("a");
resolve();
});
setTimeout(()=>console.log("d"), 0)
r.then(() => console.log("c"));
console.log("b");
実行したら、こうなります
console.log("d")より、console.log("c")が先に実行された?なぜでしょうか?
event-loop
前提として、JavaScriptはシングルスレッドで動いてるため、並行処理はできないということです。だから、ブロッキングさせないように、イベントループによってタスクキューに登録されている関数がコールスタック上に追加され、実行されます。インメージは以下のとなります。
(event-loopのインメージ from 「Help, I’m stuck in an event-loop」)
(このブログ JS基礎ー非同期とコールバック は軽くまとめましたが、ごさんこうまで)
MacroタスクとMicroタスク
promiseとsetTimeoutともコールバック関数をイベントループに登録しますが、promiseみたいなnode.js環境提供する関数はMicroタスクとしてイベントループに登録します。(恐らくブラウザ環境提供される)setTimeoutはMacroタスクとしてイベントループに登録します。
- Macroタスク:イベントループに登録できる粒度(上記の黄色のやつ)
- Microタスク:Macroタスク内格納され、より粒度が小さいタスク
promiseはMicroタスクを生成し、イベントループ内の実行待ちのMacroタスクの中にインサートしますが、setTimeoutは新たなMacroタスクを新規作成し、イベントループに登録するようなインメージです。
用途
function sleep(duration) {
return new Promise(function(resolve, reject) {
console.log("b");
setTimeout(resolve,duration);
})
}
console.log("a");
sleep(3000).then(()=>console.log("c"));
よく使われるケース:PromiseでsetTimeoutを非同期させます。
実行順番は以下となります:
➀Macroタスク1が実行
②console.log("a")が実行;
③console.log("b")が実行;
④setTimeoutでMacroタスクが登録されます
⑤resolveが実行
⑥then内のconsole.log("c")処理が実行
だから、a->b->cが出力されます。
※ sleep(0)にしても、a->b->cって順番で出力されます。
参考
1.winter.2019.relearning front-end