最後に、同期の話と非同期の話の境目を 1 つだけ確かめます。setTimeout や Promise.then に渡した関数は、それを登録した関数(a や b)の上では実行されません。
// async.js
function names() {
return new Error().stack.split('\n').slice(2)
.map((line) => line.trim().split(' ')[1]).join(' <- ');
}
function b() { console.log('同期の b の中:', names()); }
function onTimeout() { console.log('setTimeout:', names()); }
function onFulfilled() { console.log('Promise.then:', names()); }
function a() {
b();
setTimeout(onTimeout, 0);
Promise.resolve().then(onFulfilled);
}
a();
同期の b の下には a とスクリプト本体が積まれていますが、onFulfilled と onTimeout の下に a はいません。仕様も、こうした後回しの処理(ジョブ)は「実行コンテキストスタックが空になったとき」に動くと定めています。一番下に見える processTicksAndRejections や processTimers は Node 内部の関数です。仕様の言う「空」と Node 内部の関数の関係は、この記事では調べていません。