setTimeout, setInterval, setImmediate, queueMicrotask and AbortController — scheduling work and cancelling it.
| API | Schedules | Cancel with |
|---|---|---|
setTimeout(fn, ms, ...args) | Once, after at least ms | clearTimeout(id) |
setInterval(fn, ms) | Repeatedly every ms | clearInterval(id) |
setImmediate(fn) (Node) | Once, in the check phase | clearImmediate(id) |
queueMicrotask(fn) | As a microtask | — |
requestAnimationFrame(fn) (browser) | Before the next repaint | cancelAnimationFrame(id) |
const id = setTimeout(() => console.log("never printed"), 10);
clearTimeout(id);
setTimeout((a, b) => console.log("args:", a, b), 5, "x", "y");
let n = 0;
const iv = setInterval(() => {
n++;
console.log("tick", n);
if (n === 3) clearInterval(iv);
}, 5);args: x y
tick 1
tick 2
tick 3Delays are minimums
The callback runs at least
mslater — later if the stack is busy.Browsers clamp nested timers (5+ levels deep) to ≥ 4 ms and throttle background tabs to ≥ 1 s.
setIntervaldoes not wait for slow callbacks; prefer a recursivesetTimeoutwhen each run must finish before the next is scheduled.
const start = performance.now();
setTimeout(() => {
console.log("waited at least 20ms:", performance.now() - start >= 20);
}, 10);
while (performance.now() - start < 20) {} // block the thread for 20mswaited at least 20ms: truePromise-based timers
import { setTimeout as sleep } from "node:timers/promises";
const sleepAnywhere = (ms) => new Promise((r) => setTimeout(r, ms)); // browser version
console.log(await sleep(5, "node timers/promises"));
await sleepAnywhere(5);
console.log("slept");node timers/promises
sleptCancellation with AbortController
function cancellableDelay(ms, { signal } = {}) {
return new Promise((resolve, reject) => {
if (signal?.aborted) return reject(signal.reason);
const id = setTimeout(resolve, ms, "finished");
signal?.addEventListener("abort", () => {
clearTimeout(id);
reject(signal.reason);
}, { once: true });
});
}
const controller = new AbortController();
setTimeout(() => controller.abort(new Error("user cancelled")), 10);
try {
await cancellableDelay(1000, { signal: controller.signal });
} catch (e) {
console.log(e.message);
}
try {
await cancellableDelay(1000, { signal: AbortSignal.timeout(10) });
} catch (e) {
console.log(e.name);
}user cancelled
TimeoutErrorWhy might setTimeout(fn, 0) not run immediately?