Suggest an editImprove this articleRefine the answer for “The requestIdleCallback() function”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`requestIdleCallback(callback, [options])` registers a callback that the browser will run when it is not busy painting or handling events, that is, in an idle window between frames.** The browser paints a frame roughly every 16.6 ms, and if a frame only took 10 ms, about 6 ms of free time is left, and that is where your callback lands. The callback receives a `deadline` object with a `timeRemaining()` method showing how many free milliseconds are left and a `didTimeout` flag. The `{ timeout: 2000 }` option guarantees the callback runs no later than the given number of milliseconds even if no idle window ever appears. The call returns a numeric ID that is cancelled with `cancelIdleCallback(id)`. ```javascript requestIdleCallback((deadline) => { while (deadline.timeRemaining() > 0 && queue.length > 0) { process(queue.shift()); } }, { timeout: 2000 }); ``` **Key point:** `requestIdleCallback()` is a low-priority queue for background work that is not time critical and must not slow the interface down.Shown above the full answer for quick recall.Answer (EN)Image**`requestIdleCallback(callback, [options])` registers a callback that runs when the browser is not busy painting or handling events, that is, during idle time.** It is a way to do background work without stealing time from rendering and without freezing the interface. ## Theory ### TL;DR - `requestIdleCallback(fn)` puts `fn` into the idle callback queue with the lowest priority. - The callback receives a `deadline` object with `timeRemaining()` and `didTimeout`. - It is called in an idle window between frames, once rendering and microtasks have finished. - `{ timeout: N }` guarantees the call happens no later than N ms, even if no idle window appeared. - It returns a numeric ID and is cancelled with `cancelIdleCallback(id)`. - Not for critical work: on a loaded or hidden tab the call can be postponed for a long time. ### Quick example ```javascript requestIdleCallback((deadline) => { while (deadline.timeRemaining() > 0) { console.log('Doing a background task'); } console.log('The browser is busy again, waiting for the next idle window'); }); ``` Here: - `deadline` is a special object passed into the callback; - `deadline.timeRemaining()` shows how many milliseconds are still free before the next frame; - you do work **while there is time**, and if you did not finish, the browser calls the callback again in the next idle window. ### What idle time is and how the call works The browser paints frames roughly 60 times per second, that is every 16.6 ms. If some frame took only, say, 10 ms, the browser is left with **about 6 ms of free time**, and that is exactly where your `requestIdleCallback` can land. This is how you run **minor or background tasks** that are not time critical **without slowing the interface down**. Step by step: 1. You call `requestIdleCallback(fn)`. 2. The browser adds `fn` to the **idle callbacks task queue**. 3. Once the Event Loop, painting and microtasks are done and there is still time before the next frame, the browser calls your callback. 4. If the tab is inactive or there is no free time, the callback can be postponed. ### The arguments: `deadline` and `timeout` ```javascript requestIdleCallback(callback, { timeout: 2000 }); ``` - `callback(deadline)` is a function that receives an object: - `deadline.timeRemaining()`, how many milliseconds are left "until busy"; - `deadline.didTimeout`, `true` if the timeout expired and the callback was invoked under duress. - `options.timeout` is the maximum waiting time. If no free moment ever appears, the callback still runs after the given number of milliseconds. Checking `didTimeout` lets you tell the two modes apart: during a real idle window you can safely work while `timeRemaining() > 0`, whereas on a forced call `timeRemaining()` is usually zero and you should do only the bare minimum. ### Splitting heavy work into chunks ```javascript const tasks = Array.from({ length: 10000 }, (_, i) => i); function processTasks(deadline) { while (deadline.timeRemaining() > 0 && tasks.length > 0) { const task = tasks.shift(); // do a portion of the work console.log('Task processed', task); } if (tasks.length > 0) { requestIdleCallback(processTasks); } } requestIdleCallback(processTasks); ``` This approach lets you **process thousands of items** without lag: the UI stays responsive because the work happens **in the pauses between frames**. The key detail is re-registering the callback until the queue is empty, since a single call is almost never enough. ### Comparison with `setTimeout` and `requestAnimationFrame` | Criterion | `setTimeout` | `requestAnimationFrame` | `requestIdleCallback` | | --- | --- | --- | --- | | Goal | Run after N ms | Run **before** the frame is repainted | Run **in the pause between frames** | | Priority | Medium | High (for animations) | Low (background tasks) | | Depends on load | No | Yes, synchronised with the frame | Yes, called only when the browser is free | | On an inactive tab | May be throttled | Is paused | May be delayed for a long time | | Used for | Timers, delays | Animations, smooth updates | Light background computation, caching, prefetch logic | ### Browser support and a fallback `requestIdleCallback` is **not available in every browser**, Safari in particular does not have it, so it is better to keep a fallback: ```javascript const ric = window.requestIdleCallback || function (cb) { return setTimeout(() => cb({ timeRemaining: () => 0, didTimeout: true }), 1); }; ``` It **must not be used for critical tasks**, because it may not fire for a long time if the tab is under load. **Summary:** | Property | Value | | --- | --- | | **What it does** | Calls the callback when the browser has free time | | **Passes an object** | `deadline` with `timeRemaining()` and `didTimeout` | | **When it runs** | In idle windows between frames | | **Used for** | Background tasks, caching, prefetching data | | **Cancelled with** | `cancelIdleCallback(id)` | | **Returns** | A numeric ID, like timers do | ### Common mistakes - **Relying on it for critical logic.** The callback may not run for minutes if the page is constantly busy. Set a `timeout` if you need a guarantee. - **Ignoring `timeRemaining()`.** A loop without a remaining-time check eats the frame and brings back exactly the lag you were escaping. - **Not re-registering the callback.** A single idle window rarely fits all the work, so you have to call `requestIdleCallback` again at the end if the queue is not empty. - **Painting from it.** DOM changes inside an idle callback happen after the frame and cause an extra reflow. Visual updates belong in `requestAnimationFrame`. - **Forgetting about missing support.** Without a fallback the code simply does nothing in Safari, and the bug stays unnoticed for a long time. - **Not cancelling a scheduled callback.** After a component disappears the call stays in the queue, so `cancelIdleCallback(id)` is needed.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.