setTimeout Scheduling
setTimeout schedules a callback to run after at least a requested delay; it does not guarantee that the callback runs at an exact time. The callback runs when its timer is ready and the event loop can process it.
Calling `setTimeout(callback, delay)` registers a timer and returns without waiting for the callback. The delay is measured in milliseconds; a delay of `0` requests scheduling as soon as practical, not immediate execution.
When the delay has elapsed, the callback becomes eligible to run. It still has to wait for the current JavaScript work to finish and for the event loop to reach the timer callback. A long-running task can therefore make the callback late.
The callback runs as a separate task. Promise callbacks and other microtasks are generally processed before the event loop moves on to another task, so they may run before a ready timer callback.
Save the return value if you may need to cancel the timer: pass that handle to `clearTimeout`. Timers can also be delayed by browser scheduling policies, such as throttling in background tabs.
Key points
- The delay is a minimum wait, not an exact execution time.
- A timer callback cannot interrupt JavaScript that is already running.
- `setTimeout(fn, 0)` schedules a later task; it does not call `fn` immediately.
- Use the returned handle with `clearTimeout` to cancel a pending timer.
- Microtasks, such as Promise reactions, can run before a ready timer task.
Interview questions
How would you cancel a timeout that is no longer needed?
Store the handle returned by `setTimeout` and pass it to `clearTimeout`. For example, assign the result to `const timerId`, then call `clearTimeout(timerId)` before the callback runs. This is useful when a component is removed or a user action makes the delayed work irrelevant. Clearing a timeout does not undo a callback that has already started.
What does `setTimeout(callback, 1000)` guarantee?
It requests that the callback become eligible after at least about 1000 milliseconds. It does not guarantee execution at exactly that time. The callback must wait for current JavaScript work and earlier event-loop work to finish. Browser scheduling policies can add further delay.
What is the output order in a script that logs `start`, schedules a zero-delay timer, and then logs `end`?
The order is `start`, `end`, then the timer's message. The timer callback is not run inline when `setTimeout` is called. The current synchronous script must finish before the event loop can process the timer task. A zero delay means the timer is eligible as soon as practical, not that it runs immediately.
How do Promise callbacks and timer callbacks relate in event-loop ordering?
Promise reactions are microtasks, while timer callbacks are tasks. After the current JavaScript task completes, the microtask queue is processed before the event loop takes another task. Consequently, a Promise reaction queued during the current task will generally run before a zero-delay timer callback. This ordering does not make timers precise; the callback can still be delayed by other work.
Why might a timer in a background tab run later than expected?
Browsers may reduce how often timers run in background tabs to conserve resources. In addition, a busy main thread can prevent a ready callback from running promptly. Code should use timers for approximate delays, not as a precise clock or deadline mechanism. If exact elapsed time matters, measure time when the callback runs rather than assuming the requested delay was met exactly.