TypeScript & JavaScript · T252

Why does a zero timer still wait?

A zero-delay timer runs in a later task. A ready promise callback runs as a microtask after the current task, before another task starts.

The important bit
This example uses an already-resolved promise. A future network response may arrive later, and timers can be delayed by busy browser work.

Understand it. Then fix it.

Zero means later. Not right now.

Zero does not interrupt the code running now. The timer callback must wait for a later task. A task is one turn of work in the browser.

First: A, then B. The current work finishes.

The call stack tracks function calls running now. This script logs A, sets up the timer and promise callback, then logs B. Neither callback interrupts it.

Microtasks first. Then another task.

A ready promise callback is a microtask: work checked after the current task. The browser drains that queue before starting another task. So we get A, B, promise, timer.

Need that order? Put it in the code.

If a timer must follow an async step, schedule it after awaiting that step. Do not use timer delays to guess when other work will finish.

await loadData();
setTimeout(() => {
  useLoadedData();
}, 0);

Different work. Different timing.

A network response may arrive later, so this order is not a rule for every promise. Timers can also run late when the browser is busy.

Code blocks are teaching excerpts. Keep the surrounding error handling and application requirements.

Save the code excerpts ↓
Read the full transcript

Why does my browser run a timer last when I asked for zero milliseconds? Zero does not interrupt the code running now. The timer callback must wait for a later task. A task is one turn of work in the browser. The call stack tracks function calls running now. This script logs A, sets up the timer and promise callback, then logs B. Neither callback interrupts it. Then why does the promise callback get there first? I added the timer before it. A ready promise callback is a microtask: work checked after the current task. The browser drains that queue before starting another task. So we get A, B, promise, timer. If a timer must follow an async step, schedule it after awaiting that step. Do not use timer delays to guess when other work will finish. A network response may arrive later, so this order is not a rule for every promise. Timers can also run late when the browser is busy. Zero milliseconds. So the timer makes promises like my internet company.

Go to the source