A browser page schedules work with setTimeout while the user switches to another tab for ten minutes. What does the browser do to those timers, and how do you write code that survives it?
answer
- hidden tabs get clamped hard
- a wake-up hint, not a clock
- count of ticks proves nothing
- recompute from stored timestamps
- the client can never enforce a deadline
basics
~20 sHidden tabs get their timers throttled: typically no more than about once per second, and far less after several minutes in the background. Treat setTimeout as a wake-up hint and derive elapsed time from timestamps, never from how often a callback ran.
solid answer
~50 sThe browser throttles them. Once a page is not visible, timer delays are clamped to roughly one second, and after the tab has been hidden for several minutes current Chrome and Firefox go further — down to something like one wake-up per minute — to save battery. A suspended or sleeping machine runs no timers at all, and a discarded tab loses them entirely. The design consequence is that a timer callback tells you *that* you were woken, not *when* or *how many times*. So never infer elapsed time from a firing count or from the delay you requested: capture a timestamp when you start, and compare it with `Date.now()` inside the callback to compute what actually elapsed. Anything with real consequences — a session expiry, a rate limit, a deadline — must be enforced where a hidden tab cannot influence it, which in practice means the server.
code
javascript · 9 linesconst deadline = Date.now() + 5 * 60 * 1000;
function tick() {
const msLeft = deadline - Date.now();
console.log('seconds left:', Math.max(0, Math.ceil(msLeft / 1000)));
if (msLeft > 0) setTimeout(tick, Math.min(1000, msLeft));
}
tick(); // stays correct even if the tab is hidden and throttledgo deeper
Know that a hidden browser tab has its timers slowed down, so a countdown or poll driven purely by setTimeout falls behind while the user is elsewhere.
Explain the layers — a clamp to about a second when hidden, tighter throttling after minutes, nothing at all while suspended — and show the fix: compute remaining time from a stored timestamp instead of counting callbacks.
Demonstrate production judgment. Diagnose throttling from lateness correlated with visibility, design polling that reconciles in one request when the page returns, and keep anything with consequences off the client clock.
Own the boundary: client timers are advisory and user-controllable, so every deadline that carries risk is enforced server-side. Weigh battery and backend cost of background polling against a reconcile-on-return design, and set that policy across the app.
## What throttling actually is A background tab is doing nothing a user can see, so browsers deliberately stop honouring its timers at the requested rate. The policy has layers: - **Hidden-tab clamping.** As soon as the page is not visible, timers are clamped to a minimum of roughly one second, regardless of the delay you asked for. - **Intensive throttling.** After the tab has been hidden for several minutes, modern Chrome and Firefox tighten further, allowing timer wake-ups on the order of once per minute, aligned so many pages wake together and the CPU can stay idle in between. - **Suspension.** A sleeping or hibernating machine executes nothing. On wake, timers do not "catch up" by firing once per missed interval; you simply get the next wake-up, very late. - **Discard.** Under memory pressure a background tab may be dropped entirely. Its timers are gone; a reload starts from scratch. These are host policies, not language rules, and the exact thresholds move between browser versions. What is stable — and what an interviewer is testing — is the shape: **timers in the background are unreliable in the late direction, by orders of magnitude, and you cannot opt out.** ## The bug this produces The classic failure is code that treats timer firings as a clock: ```js // broken: assumes every tick really happened let secondsLeft = 300; setInterval(() => { secondsLeft -= 1; render(secondsLeft); }, 1000); ``` Leave the tab for ten minutes and come back: the counter has barely moved, because the callback ran a fraction of the times it "should" have. Users see a countdown that pauses whenever they look away, a poll that silently stops, a "session expires in…" banner that is wildly wrong, or an auto-save that never fires. The fix is to make the timer a *prompt to recompute* rather than a unit of measurement: ```js const deadline = Date.now() + 300_000; function tick() { const msLeft = deadline - Date.now(); render(Math.max(0, Math.ceil(msLeft / 1000))); if (msLeft > 0) setTimeout(tick, Math.min(1000, msLeft)); } tick(); ``` Now however late or infrequent the wake-ups are, the number displayed is correct the moment it is drawn, and the code converges as soon as the tab is visible again. The same principle applies to any polling loop: on each wake-up, ask "how long has it actually been, and what do I owe as a result?" rather than "one more interval has passed". ## Correctness versus display There is a second, sharper consequence. If a hidden tab can stop your timers, then a user can trivially stop them — by switching tabs, or with the devtools open. So a client timer can never be the thing that *enforces* anything: - A session or token expiry is enforced by the server rejecting the credential, not by a timer clearing local state. The timer is a courtesy that shows the user a message. - A rate limit or cooldown is enforced by whatever the request reaches. A disabled button on a timer is presentation. - A quiz or auction deadline is decided by comparing server-side timestamps on submission. Saying this out loud is usually what separates a senior answer from a merely correct one: throttling is not only a UX bug, it is a reminder that client-side timing is advisory. ## Diagnosing it The signature is distinctive and easy to confirm. Log `performance.now()` at scheduling time and at callback entry, and look at the distribution: - Lateness flattening at about 1000 ms, appearing only when the page is hidden and vanishing when it is shown, is hidden-tab clamping. - Lateness in the tens of seconds after minutes away is intensive throttling. - A single enormous gap that matches how long the laptop was closed is suspension. - Lateness in the hundreds of milliseconds while the page is visible is not throttling at all — that is long synchronous work holding the thread, a different problem with a different fix. ## What to do about work that genuinely must continue The honest answer for a hidden tab is often "it should not". If a page is not visible, the useful design is usually to stop polling entirely, then reconcile in one shot when the page becomes visible again — one request that fetches everything missed, rather than a backlog of ticks. That is both cheaper and more correct than fighting the throttle. When work must continue independently of a tab's visibility, it belongs somewhere that is not a page timer — on the server, or in a mechanism designed for background execution — and that choice is outside what a `setTimeout`-shaped answer can fix.
- After a laptop sleeps for an hour with a page open, do the missed timer firings replay on wake?No. Nothing runs while the machine is suspended, and there is no catch-up burst afterwards — you get the next wake-up, arriving very late. Code that assumed one callback per interval sees a single jump. Recomputing from a stored timestamp handles it naturally, because the elapsed time is read from the clock rather than counted.
- How do you tell throttling apart from your own code blocking the event loop?Correlate lateness with page visibility. Throttling only appears while the page is hidden, and the lateness snaps to a characteristic floor — around a second, or tens of seconds once intensive throttling kicks in. Blocking shows up while the page is visible, scales with the size of the work, and comes with matching long-task and input-latency symptoms.
- A countdown must end at a fixed moment, and the user leaves the tab for the whole duration. What is the right design?Store the absolute deadline, not a remaining count. On every wake-up, and again when the page becomes visible, compute the remaining time from the current clock and render that. Treat the client display as advisory and have the server compare timestamps when the submission arrives, so the outcome does not depend on whether the tab was throttled.
saying these in an interview costs you the question
- Says timers keep firing normally in a background tab
- Counts callback invocations to measure elapsed time
- Expects missed firings to replay after a laptop wakes
- Thinks a shorter requested delay defeats throttling
- Enforces a session expiry purely with a client-side timer