私のエクスプレスコード:
app.use('/test', async (req, res, next) => {
try {
setTimeout(() => console.log('setTimeout')); // setTimeout (macrotask)
User.findOne().then(user => console.log('user')); // promise (microtask)
console.log('console.log');
} catch (err) {
next(err);
}
});
コンソールの出力順序は次のとおりです。
console.log
setTimeout
user
質問:なぜマイクロタスクはマクロタスクの後に実行されるのですか?
たとえば、ブラウザでは、次のコードが正しい順序で解決されます。
コード:
setTimeout(function timeout() {
console.log(3);
}, 0);
let p = new Promise(function (resolve, reject) {
for (let i = 0; i < 1e10; i++) {}
resolve();
});
p.then(function () {
console.log(2);
});
console.log(1);
注文:
1
2
3
マイクロタスクを最初に実行するには、マクロタスクをマクロタスクキューからデキューする前に、マイクロタスクをキューに入れる必要があります。.findOne()
時間がかかるため、マイクロタスクは.findOne()
、setTimeout
コールバックがマクロタスクキューに追加されてからコールスタックにデキューされて実行された後に発生する、resolvesによって返されるpromiseまでキューに入れられません。
.findOne()
作成しているPromiseのエグゼキュータ関数が同期的に実行されるため、「動作する」コードはNodeプログラムの状況とは異なります(実際、このコードはNodeでも1、2、3を生成することがわかります) :
setTimeout(function timeout() { // <--- queue `timeout` callback as a macro-task
console.log(3);
}, 0);
let p = new Promise(function (resolve, reject) { // run this function synchronously
for (let i = 0; i < 1e10; i++) {} // <--- wait here for the loop to complete
resolve();
});
// Only reached once the loop above has complete, as thus, your promise has resolved
p.then(function () { // <--- `p` has resolved, so we queue the function as a micro-task
console.log(2);
});
console.log(1); // <--- add the function to call stack (first log to execute)
上記では、スクリプトの実行中に、setTimeoutコールバックと.then()コールバックの両方がそれぞれのタスクキューに追加されるため、スクリプトの実行が終了すると、マイクロタスクをデキューしてスタックに配置できます。 、次に、マクロタスクを要求してスタックに配置できます。
あなたのコードは異なります:
/*
setTimeout gets added to the callstack, that spins off an API which after 0 m/s adds your callback to the macro-task queue
*/
setTimeout(() => console.log('setTimeout'));
/*
.findOne() gets added to the call stack, that spins off an API which after N m/s adds the `.then()` callback to the micro-task queue (N > 0)
*/
User.findOne().then(user => console.log('user')); // promise (microtask)
/*
While the above API is working in the background, our script can continue on...
console.log() gets added to the call stack and "console.log" gets logged
*/
console.log('console.log');
上記のスクリプトが終了すると、timeout
コールバックはマクロタスクキューにあり.then()
ますが、クエリはまだバックグラウンドで実行されているため、コールバックはまだマイクロタスクキューにありません。次に、timeout
コールバックはコールスタックにデキューされます。しばらくすると、.then()
コールバックがマイクロタスクキューに追加され、コールスタックが空になると実行され、キューからスタックに移動されます。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加