Copyright © 2015 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use 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. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This is a work in progress and may change without any notices.
Implementers SHOULD be aware that this document is not stable. Implementers who are not taking part in the discussions are likely to find the specification changing out from under them in incompatible ways. Vendors interested in implementing this document before it eventually reaches the Candidate Recommendation stage SHOULD join the mailing lists below and take part in the discussions.
This document was published by the Web Performance Working Group as a First Public Working Draft.
This document is intended to become a W3C Recommendation.
If you wish to make comments regarding this document, please send them to
public-web-perf@w3.org
(subscribe,
archives)
with [RequestIdleCallback]
at the start of your email's subject.
All comments are welcome.
Publication as a First Public Working Draft does not imply endorsement by the W3C Membership. 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 5 February 2004 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 1 September 2015 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 to localStorage
[webstorage], 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
[animation-timing]. 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 any 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 Fig. 1 Example of an inter-frame idle period. 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 user 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 Fig. 2 Example of an idle period when there are no pending frame updates, 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. Only idle tasks which were posted before the start of the current idle period are eligible to 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. 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.
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. In
order to provide script authors with a guarantee that these
callbacks will be run, the requestIdleCallback
API
provides an optional argument specifying a timeout by which the
callback should be run.
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 are to be interpreted as described in [RFC2119].
The IDL fragments in this specification MUST be interpreted as required for conforming IDL fragments, as described in the Web IDL specification. [webidl]
This specification defines a single conformance class:
The partial interface in the IDL fragment below is used to expose the requestIdleCallback
operation on the Window
object. In the definition of requestIdleCallback
below, references to the Document
object [dom] are to be taken to be references to the Window
object's active document. [HTML5]
partial interface Window { unsigned long requestIdleCallback(IdleRequestCallback
callback, optional unsigned long timeout); void cancelIdleCallback(unsigned long handle); }; interface IdleDeadline { DOMHighResTimeStamp timeRemaining(); readonly attribute boolean didTimeout; }; callback IdleRequestCallback = void (IdleDeadline
deadline);
Each Document
has:
When requestIdleCallback(callback, timeout)
is invoked, the user agent MUST run the following steps:
Window
object's
active document object. [HTML5]
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 called, the user agent MUST run the following steps:
Window
object's
active document object. [HTML5]
cancelIdleCallback
might be invoked for an entry in the document's list of idle request callbacks or the document's list of runnable idle callbacks . In either case the entry should be removed from the list so that the callback does not run.
When the timeRemaining()
function is called on an IdleDeadline
object it MUST return the amount of time remaining before the callback's deadline as a DOMHighResTimeStamp.
This value is calculated by performing the following steps:
DOMHighResTimeStamp
. [hr-time-2]performance.now()
. [hr-time-2]The didTimeout
attribute on an IdleDeadline
object MUST return true
if the callback was invoked by the invoke idle callback timeout algorithm, and false
otherwise.
Whenever the user agent assesses that a given event loop is likely to remain idle for a non-trivial amount of time, it SHOULD initiate a new idle period for the event loop.
The expectation is that the user agent will initiate idle periods regularly when the event loop becomes idle, for example, in between frame rendering and regularly during times when no frames are being rendered. If the Document
's hidden
attribute
[page-visibility] is false 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.
When the user agent wishes to start an event loop's idle period, the following steps MUST be performed:
Document
objects associated with the event loop in question, sorted arbitrarily.The task source for these tasks is the idle-task task source.
The time between now and deadline is referred to as the idle period. There can only be one idle period active at a given time. 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.
Also note, the expectation is
that the user agent will choose deadline to ensure that no time-critical
tasks will be delayed even if a callback runs for the whole time
period from now to deadline. As such, it should be set
to the minimum of: the closest timeout in the
list of active timers as set via
setTimeout
and setInterval
; the scheduled runtime
for pending animation callbacks posted via requestAnimationFrame
[animation-timing];
pending internal timeouts such as deadlines to start rendering the
next frame, process audio or any
other internal task the user agent deems important; and a maximum
cap of 50ms in the future to ensure responsiveness to unpredictable
user input within the threshold of human perception.
The invoke idle callbacks algorithm:
IdleDeadline
constructed
with the given deadline and the didTimeout
attribute set to false
The user agent is free to end an idle period early, even if deadline has not yet occurred, by not queuing the invoke idle callbacks algorithm continuation task in step 2.4 of the invoke idle callbacks algorithm. 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
constructed with a deadline of now and the didTimeout
attribute set to true
.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, Todd Reifsteck, Tobin Titus, Ilya Grigorik, Elliott Sprehn, Tetsuharu OHZEKI, Lon Ingram and Philippe Le Hegaret.