What is requestIdleCallback?
In the same way that adopting requestAnimationFrame allowed us to schedule animations properly and maximize our chances of hitting 60fps, requestIdleCallback will schedule work when there is free time at the end of a frame, or when the user is inactive.
Glossary
1.0 Why?
2.0 How?
2.1 The requestIdleCallback
Function
2.2 Deadline Object and the Idle timeRemaining
Property
2.3 Wrapping rIC with Promises
4.0 Availability
5.0 References
1. Why?
As JavaScript is executed on a single thread any computation causes the application to wait until it has completed before moving on and performing another. There is no standard for delaying execution of a procedure until the environment is idle… until now! When an environment is idle can be the best time to execute tasks that don’t make an immediate and obvious contribution to the user’s experience. With requestIdleCallback
[1] we can supply a callback to be invoked when the environment isn’t occupied with anything else and therefore avoid interupting user interactions.
2. How?
2.1 The requestIdleCallback
Function
We simply pass an anonymous function or function reference to requestIdleCallback
. We also have the option of a second argument that specifies the maximum amount of time to wait before executing the callback outright, regardless of whether the browser is idle. This is really useful for tasks that are necessary without requiring immediate attention.
1 | // Passing an anonymous function |
2.2 Deadline Object
You may have noticed in each of the examples above that the callback is invoked with a single argument deadline
. This is an object with two properties: timeRemaining
and didTimeout
.
timeRemaining
timeRemaining
contains the amount of time that the function has left before the environment is no longer idle (in milliseconds). It is updated dynamically and so can be inspected within the procedure via a while
loop or similar such that it will only perform its operations during the available idle window. This is necessary because as with any function the browser cannot prematurely destroy your callback. You are therefore responsible for ending execution of the callback (via a return
) at an appropriate time (timeRemaining >= 0
) so that it does not occupy more than is reported by the browser as being idle! If you don’t do this you will start muscling in on time that would have been used for other operations.
1 | requestIdleCallback(function someHeavyComputation(deadline) { |
didTimeout
didTimeout
is a boolean that if true
means that the callback has been invoked because an idle window was not available within the deadline provided via requestIdleCallback
‘s second argument. If you have provided this argument and the amount of time declared by it has passed you will want to perform the callback’s work by checking the didTimeout
property and doing the work regardless.
1 | // Declare callback to be invoked after two seconds if |
2.3. Wrapping rIC with Promises
You can of course execute any code within a requestIdleCallback
(rIC), but if the operation you are performing is computationally expensive and will therefore take a long time to complete, consider wrapping it in a Promise [2]. Doing this will abstract a linear operation that would otherwise be blocking and will make it obviously asynchronous.
1 | function factorial(input) { |
4. Availability
Currently this feature is only available in Chrome Canary (with chrome://flags/#enable-experimental-web-platform-features enabled). But there have been talks with the other browser vendors Microsoft and Apple about implementing this in their own browsers. [3]
Chrome (Canary) | Firefox | Safari | Opera |
---|---|---|---|
Yes | No | No | No |
While availablity is poor you will need to always check that the requestIdleCallback
API is available with a simple if block.
1 | if ('requestIdleCallback' in window) { |
5. References
[1] “Using requestIdleCallback” on the Google Developers Blog
[2] Promise Ponderings, (Anti-)Patterns, and Apologies
[3] Paul Lewis on Twitter