window.requestIdleCallback() all you need to know
There is a native js function that you can use in almost every browser except Safari to execute work that shouldn't interrupt user experience. Current support for this function in all browsers you can check on caniuse site.
Imagine that you have some work in your application that can be executed not immediately but while there is some idle in the browser work. As an example, sending analytics data. In this case, some time ago you could try to check for the user events and execute your delayed work once the user doesn't do anything. But there is no good way for a developer to predict if the browser is idle because we cannot calculate time for style calculations, layout, paint and other internal processes.
And to help us make sure that we can do some work without interrupting user experience browsers introduced window.requestIdleCallback()
function.
This function accepts 2 parameters:
callback that should be executed once the browser idles
optional parameter - timeout for the callback
The best is to not pass the timeout and let the browser choose the best time to execute your callback. But if you have some limitations in how long you can wait then the timeout is the way to go.
window.requestIdleCallback(processPendingAnalyticsEvents, { timeout: 2000 });
The callback itself accepts also one parameter called deadline
and here you can find two useful fields:
deadline.timeRemaining()
- get the amount of time left to execute your workdeadline.didTimeout
- will be equal totrue
if the browser waited for thetimeout
and this timeout already finished
The workflow can look like this
The user triggers some event, like a button click
onClick = () => {... requestIdleCallback(callback);}
When trying to maintain a responsive application, all responses to user interactions should be kept under 100ms. So the browser will split all its work into cycles not longer than 100 ms and try to do some internal work and also execute your js code. Once the browser founds free time in one of such cycles it will call your callback from requestIdleCallback
. Or if you set the timeout
and it is already passed, then it will schedule your callback anyway. Inside of your callback, you can do some work, but try not to overrun the deadline, so you won't break the smooth user experience
callback (deadline) {
while (deadline.timeRemaining()>0 || deadline.didTimeout) {
doSomeWork()
}
if (thereIsWorkLeft) {
requestIdleCallback(newCallback)
}
}
With this approach, you should split your work into microtasks and in this way, you can guarantee a smooth user experience. You can create as many calls requestIdleCallback
as you need, also you can do it from requestIdleCallback
itself.
Longest time to run your callback
The biggest time allowed for your callback is 50ms you can easily check it with such code, that you can execute on any page
window.f = 0
function callback(deadline) {
console.log(deadline.timeRemaining());
window.f++;
if (window.f < 10) window.requestIdleCallback(callback)
}
window.requestIdleCallback(callback)
For such code, you will get different results each time but it will never be bigger than 50ms. The reason for this you will learn from one of my next posts, where I will explain the event loop in JS.
Tips
Also, I would like to note that you shouldn't use requestIdleCallback
it for something that has unpredictable characteristics. For example, for DOM interactions, you should requestAnimationFrame()
because it will add work related to style calculations, layout, and paint. Also, it's better not to resolve or reject Promises, since their callbacks will be executed immediately after the current callback is finished and you can't predict if there is enough time for their execution.
Summary
So, if you have some work that you can delay to guarantee a smooth user experience, you can use requestIdleCallback
where you will send some micro tasks that can be executed until there is some time remaining and once the time is out you can schedule the next requestIdleCallback
to continue with your work.