Note: This is not a duplicate of questions about blocking or long-running loops. There is no CPU-intensive or infinite loop here — the issue is that
Promise.all
is unexpectedly delayed due to unrelatedqueueMicrotask()
calls. It’s a subtle event loop/microtask interaction, not a case of blocking code.
I’m working with some async code where all Promises are already resolved, but there’s also a queue of microtasks triggered before Promise.all runs. Surprisingly, Promise.all still seems to wait for all of them to finish before resolving, even though the Promises themselves are resolved instantly.
Here’s a minimal repro:
// This is a synchronous busy loop that blocks the event loop
function busyLoop(iterations) {
const start = Date.now();
while (Date.now() - start < iterations) {
// intentionally blocking
}
}
function microtaskFlood() {
for (let i = 0; i < 1000; i++) {
queueMicrotask(() => {
// Just a microtask that does nothing
});
}
}
async function test() {
console.log('Start');
busyLoop(100); // Blocks event loop synchronously for ~100 ms
// Schedule many microtasks after blocking
microtaskFlood();
// Promise.all waits for these microtasks to finish
await Promise.all([
Promise.resolve('done1'),
Promise.resolve('done2')
]);
console.log('End');
}
test();
Expected:
Since both a and b are resolved, I expected Promise.all to resolve immediately.
Actual:
The Promise.all .then() only fires after the microtask finishes. This seems unintuitive because the microtask has no relation to a or b.
Questions:
- Is this behavior according to the ECMAScript spec?
- Why doesn’t Promise.all resolve in the same tick when all Promises are already resolved?
- Is there a way to make it resolve without waiting on unrelated microtasks?
Any explanation about how JavaScript’s microtask queue affects Promise.all scheduling would help.
Thanks!