Node.js:循环期间如何允许进程更新?代码超时未生效问题排查
Let's break down what's happening here and why your code isn't behaving as expected.
First, here's your original code for reference:
var cancelled = false; setTimeout(() => cancelled = true,1000); function main() { var ret = [] var totalStart =Date.now(); for(var i=0;i<20;i++) { var v var start = Date.now(); while((Date.now()-start)<100) { v = Math.sqrt(Math.random()); } ret.push(v); if(cancelled) break; } console.log("delta:"+(Date.now()-totalStart)); return ret; } var r = main() console.log(r.length)
The Root Cause: Node.js's Single-Threaded Event Loop
Node.js operates on a single thread with an event-driven model, and that's where the problem lies:
- Your
main()function is synchronous blocking code. The outerforloop and innerwhileloop run nonstop, hogging the only available thread from start to finish. - The
setTimeoutcallback (() => cancelled = true) gets scheduled to run after 1000ms, but it can only execute once the current execution stack is empty. Sincemain()takes ~2000ms to finish all its synchronous work, the callback doesn't run until the loop has already completed all 20 iterations. By then, checkingif(cancelled)does nothing— the loop is already done.
How to Fix It: Make the Loop Non-Blocking
To let the event loop process the setTimeout callback mid-loop, you need to break up the synchronous work into chunks that give the event loop a chance to run. Here's a modified version using async/await to yield control to the event loop after each iteration:
let cancelled = false; setTimeout(() => cancelled = true, 1000); async function main() { const ret = []; const totalStart = Date.now(); for (let i = 0; i < 20; i++) { if (cancelled) break; let v; const start = Date.now(); while ((Date.now() - start) < 100) { v = Math.sqrt(Math.random()); } ret.push(v); // Yield control so the event loop can process the setTimeout callback await new Promise(resolve => setImmediate(resolve)); } console.log("delta:" + (Date.now() - totalStart)); return ret; } main().then(r => console.log(r.length));
Here's what changed and why:
- We turned
maininto an async function, which lets us useawaitto pause the loop temporarily. - After each iteration, we
awaitasetImmediatepromise. This tells Node.js to pause the loop, handle any pending events (like thesetTimeoutcallback), and then resume the loop. - Now, when the 1000ms mark hits, the
setTimeoutcallback will run between iterations, settingcancelledtotrue, and the next loop check will break out of the loop as you expected.
If you prefer not to use async/await, you could also use recursive setTimeout calls to handle each iteration asynchronously, but async/await keeps the code structure close to your original while fixing the blocking issue.
内容的提问来源于stack exchange,提问作者Perry

