requestIdleCallback()
Copyright © 2022 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and permissive document license rules apply.
This document defines an API that web page authors can use to cooperatively schedule background tasks such that they do not introduce delays to other high priority tasks that share the same event loop, such as input processing, animations and frame compositing. The user agent is in a better position to determine when background tasks can be run without introducing user-perceptible delays or jank in animations and input response, based on its knowledge of currently scheduled tasks, vsync deadlines, user-interaction and so on. Using this API should therefore result in more appropriate scheduling of background tasks during times when the browser would otherwise be idle.
This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
The Web Performance Working Group maintains a test suite for the specification. Please see the Working Group's implementation report. Vendors interested in implementing this document SHOULD join the mailing lists below and take part in the discussions.
This document was published by the Web Performance Working Group as a Working Draft using the Recommendation track.
Publication as a Working Draft does not imply endorsement by W3C and its Members.
This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
This document is governed by the 2 November 2021 W3C Process Document.
This section is non-normative.
Web pages often want to execute computation tasks on the browser's event
loop which are not time-critical, but might take a significant portion of
time to perform. Examples of such background tasks include recording
analytics data, long running data processing operations, client-side
templating and pre-rendering of content likely to become visible in the
near future. These tasks must share the event loop with other time-critical
operations, such as reacting to input and performing script-based
animations using requestAnimationFrame
()
. These
background tasks are typically performed by scheduling a callback using
setTimeout
()
and running the background task during that
callback.
A disadvantage of this approach is that the author of the script has no
way to inform the user-agent as to whether a given setTimeout
()
callback is
time-critical or could be delayed until the browser is otherwise idle. In
addition, the user agent isn't able to provide the callback with any
information about how long it can continue to execute without delaying
time-critical operations and causing jank or other user-perceptible delays.
As a result, the easiest way forward is for the author is to simply call
setTimeout
()
with a very small value, and then execute the minimum possible
chunk of work in the resulting callback and reschedule additional work with
another call to setTimeout
()
. This is less than optimal because there is
extra overhead from having to post many small tasks on the user agent's
event loop and schedule their execution. It also relies on the user-agent
interleaving other time-critical work between each of these callbacks
appropriately, which is difficult since the user-agent can't make accurate
assumptions on how long each of these callbacks are likely to take.
The API described in this document allows script authors to request the user-agent to schedule a callback when it would otherwise be idle. The user agent provides an estimation of how long it expects to remain idle as a deadline passed to the callback. The page author can use the deadline to ensure that these background tasks don't impact latency-critical events such as animation and input response.
Here is an example of using the API to write a background task.
<!DOCTYPE html>
<title>Scheduling background tasks using requestIdleCallback</title>
<script>
var requestId = 0;
var pointsTotal = 0;
var pointsInside = 0;
function piStep() {
var r = 10;
var x = Math.random() `*` r `*` 2 - r;
var y = Math.random() `*` r `*` 2 - r;
return (Math.pow(x, 2) + Math.pow(y, 2) < Math.pow(r, 2))
}
function refinePi(deadline) {
while (deadline.timeRemaining() > 0) {
if (piStep())
pointsInside++;
pointsTotal++;
}
currentEstimate = (4 `*` pointsInside / pointsTotal);
textElement = document.getElementById("piEstimate");
textElement.innerHTML="Pi Estimate: " + currentEstimate;
requestId = window.requestIdleCallback(refinePi);
}
function start() {
requestId = window.requestIdleCallback(refinePi);
}
function stop() {
if (requestId)
window.cancelIdleCallback(requestId);
requestId = 0;
}
</script>
<button onclick="start()">Click me to start!</button>
<button onclick="stop()">Click me to stop!</button>
<div id="piEstimate">Not started</div>
This section is non-normative.
After input processing, rendering and compositing for a given frame has
been completed, the user agent's main thread often becomes idle until
either: the next frame begins; another pending task becomes eligible to
run; or user input is received. This specification provides a means to
schedule execution of callbacks during this otherwise idle time via a
requestIdleCallback
()
API.
Callbacks posted via the requestIdleCallback
()
API become eligible to
run during user agent defined idle periods. When an idle callback is run it
will be given a deadline which corresponds to the end of the current idle
period. The decision as to what constitutes an idle period is user agent
defined, however the expectation is that they occur in periods of
quiescence where the browser expects to be idle.
One example of an idle period is the time between committing a given frame to the screen and starting processing on the next frame during active animations, as shown in Figure 1. Such idle periods will occur frequently during active animations and screen updates, but will typically be very short (i.e., less than 16ms for devices with a 60Hz vsync cycle).
The web-developer should be careful to account for all work performed by operations during an idle callback. Some operations, such as resolving a promise or triggering a page layout, may cause subsequent tasks to be scheduled to run after the idle callback has finished. In such cases, the application should account for this additional work by yielding before the deadline expires to allow these operations to be performed before the next frame deadline.
Another example of an idle period is when the user agent is idle with no screen updates occurring. In such a situation the user agent may have no upcoming tasks with which it can bound the end of the idle period. In order to avoid causing user-perceptible delays in unpredictable tasks, such as processing of user input, the length of these idle periods should be capped to a maximum value of 50ms. Once an idle period is finished the user agent can schedule another idle period if it remains idle, as shown in Figure 2, to enable background work to continue to occur over longer idle time periods.
During an idle period the user agent will run idle callbacks in FIFO order until either the idle period ends or there are no more idle callbacks eligible to be run. As such, the user agent will not necessarily run all currently posted idle callbacks within a single idle period. Any remaining idle tasks are eligible to run during the next idle period.
To deliver the best performance developers are encouraged to eliminate unnecessary callbacks (e.g. requestAnimationFrame, setTimeout, and so on) that do not perform meaningful work; do not keep such callbacks firing and waiting for events to react, instead schedule them as necessary to react to events once they become available. Such pattern improves overall efficiency, and enables the user agent to schedule long idle callbacks (up to 50ms) that can be used to efficiently perform large blocks of background work.
Only idle tasks which posted before the start of the current idle period
are eligible to be run during the current idle period. As a result, if an
idle callback posts another callback using requestIdleCallback
()
, this
subsequent callback won't be run during the current idle period. This
enables idle callbacks to re-post themselves to be run in a future idle
period if they cannot complete their work by a given deadline - i.e.,
allowing code patterns like the following example, without causing the
callback to be repeatedly executed during a too-short idle period:
function doWork(deadline) {
if (deadline.timeRemaining() <= 5) {
// This will take more than 5ms so wait until we
// get called back with a long enough deadline.
requestIdleCallback(doWork);
return;
}
// do work...
}
At the start of the next idle period newly posted idle callbacks are appended to the end of the runnable idle callback list, thus ensuring that reposting callbacks will be run round-robin style, with each callback getting a chance to be run before that of an earlier task's reposted callback.
Future versions of this specification could allow other scheduling strategies. For example, schedule idle callback within the same idle period, a period that has at least X milliseconds of idle time, and so on. Current specification only supports the case for scheduling into the next idle period, at which time the callback can execute its logic, or repost itself into the next idle period.
When the user agent determines that the web page is not user visible it can throttle idle periods to reduce the power usage of the device, for example, only triggering an idle period every 10 seconds rather than continuously.
A final subtlety to note is that there is no guarantee that a user agent
will have any idle CPU time available during heavy page load. As such, it
is entirely acceptable that the user agent does not schedule any idle
period, which would result in the idle callbacks posted via the
requestIdleCallback
()
API being postponed for a potentially unbounded
amount of time. For cases where the author prefers to execute the callback
within an idle period, but requires a time bound within which it can be
executed, the author can provide the timeout
property in the options
argument to requestIdleCallback
()
: if the specified timeout is reached
before the callback is executed within an idle period, a task is queued to
execute it.
The maximum deadline of 50ms is derived from studies [RESPONSETIME] which show that that a response to user input within 100ms is generally perceived as instantaneous to humans. Capping idle deadlines to 50ms means that even if the user input occurs immediately after the idle task has begun, the user agent still has a remaining 50ms in which to respond to the user input without producing user perceptible lag.
As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.
The key words MUST, REQUIRED, SHALL, and SHOULD in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
The IDL fragments in this specification MUST be interpreted as required for conforming IDL fragments.
This specification defines a single conformance class:
Window
interface extensions.
Window
interface extensionsThe partial interface in the IDL fragment below is used to expose the
requestIdleCallback
()
operation on the
Window
object.
WebIDLpartial interface Window {
unsigned long requestIdleCallback
(IdleRequestCallback
callback, optional IdleRequestOptions
options = {});
undefined cancelIdleCallback
(unsigned long handle);
};
dictionary IdleRequestOptions
{
unsigned long timeout
;
};
[Exposed=Window] interface IdleDeadline
{
DOMHighResTimeStamp timeRemaining
();
readonly attribute boolean didTimeout
;
};
callback IdleRequestCallback
= undefined (IdleDeadline
deadline);
Each Window
has:
Window
object.Window
object.When requestIdleCallback
(callback, options)
is
invoked with a given IdleRequestCallback
and optional IdleRequestOptions
, the user agent
MUST run the following steps:
Window
object.The following steps run in parallel and queue a timer
similar to setTimeout()
if the optional timeout
property is
provided. From here, the idle and timeout callbacks are raced and
cancel each other—e.g. if the idle callback is scheduled first, then
it cancels the timeout callback, and vice versa.
timeout
property is present in options and has a positive
value:
This is intended to allow user agents to pad timeouts as needed to optimise the power usage of the device. For example, some processors have a low-power mode where the granularity of timers is reduced; on such platforms, user agents can slow timers down to fit this schedule instead of requiring the processor to use the more accurate mode with its associated higher power usage.
requestIdleCallback
()
only schedules a single callback, which will be executed during a
single idle period. If the callback cannot complete its work before
the given deadline then it should call
requestIdleCallback
()
again
(which may be done from within the callback) to schedule a future callback
for the continuation of its task, and exit immediately to return control
back to the event loop.
The cancelIdleCallback
()
method is used to cancel a
previously made request to schedule an idle callback. When
cancelIdleCallback
(handle)
is invoked, the user agent
MUST run the following steps:
Window
object.cancelIdleCallback
()
might be invoked for an entry in window's list of
idle request callbacks or the list of runnable idle callbacks.
In either case the entry should be removed from the list so that the
callback does not run.
Each IdleDeadline
has an associated get deadline time algorithm, which
returns a DOMHighResTimeStamp
representing the absolute time in
milliseconds of the deadline. The deadline is initially set to return zero.
When the timeRemaining()
method is invoked on
an IdleDeadline
object it MUST return the remaining duration before
the deadline expires as a DOMHighResTimeStamp
, which SHOULD be
enough to allow measurement while preventing timing attack - see
"Privacy and Security" section of [HR-TIME]. This value is calculated
by performing the following steps:
DOMHighResTimeStamp
representing
current high resolution time in milliseconds.IdleDeadline
's get deadline time algorithm.Each IdleDeadline
has an associated timeout, which is
initially false. The didTimeout
getter MUST
return timeout.
The invoke idle callback timeout algorithm sets
timeout to true to specify that the callback is being executed
outside an idle period, due to it having exceeded the value of the
IdleRequestOptions
's timeout property which was passed when the
callback was registered.
To start an idle period given Window
window and
getDeadline, an algorithm that returns a DOMHighResTimeStamp
: [HR-TIME]
The algorithm is called by the event loop processing model when it determines that the event loop is otherwise idle.
This is intended to allow user agents to delay the
start of idle periods as needed to optimise the power usage of the
device. For example, if the Document
's visibility state is "hidden" then the user agent can
throttle idle period generation, for example limiting the Document to
one idle period every 10 seconds to optimize for power usage.
The time between now and the deadline is referred
to as the idle period. There can only be one idle period
active at a given time for any given Window
. The idle period can end
early if the user agent determines that it is no longer idle. If so,
the next idle period cannot start until after deadline.
To invoke idle callbacks algorithm given Window
window and
getDeadline, an algorithm returning a DOMHighResTimeStamp
: [HR-TIME].
IdleDeadline
whose
is getDeadline.The user agent is free to end an idle period early, even if deadline has not yet occurred, by deciding return from the algorithm in step 1. For example, the user agent may decide to do this if it determines that higher priority work has become runnable.
The invoke idle callback timeout algorithm:
IdleDeadline
.
Set the get deadline time algorithm associated with deadlineArg to
an algorithm returning now and set the timeout associated with
deadlineArg to true
.When an idle callback is scheduled the user agent provides an estimate
of how long it expects to remain idle. This information can be used to
estimate the time taken by other application tasks and related browser work
within that frame. However, developers can already access this information
via other means - e.g. mark beginning of the frame via
requestAnimationFrame
, estimate the time of the next frame, and use that
information to compute "remaining time" within any callback.
To mitigate cache and statistical fingerprinting attacks, the resolution
of the time estimates returned by the IdleDeadline
interface should
be coarsened based on
cross-origin isolated capability to ensure this API is not
exposing more information than what's exposed by other web-exposed timers. [HR-TIME]
TBD
The editors would like to thank the following people for contributing to this specification: Sami Kyostila, Alex Clarke, Boris Zbarsky, Marcos Caceres, Jonas Sicking, Robert O'Callahan, David Baron, Todd Reifsteck, Tobin Titus, Elliott Sprehn, Tetsuharu OHZEKI, Lon Ingram, Domenic Denicola, Philippe Le Hegaret and Anne van Kesteren.
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: