window.requestIdleCallback() all you need to know

·

3 min read

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 work

  • deadline.didTimeout - will be equal to true if the browser waited for the timeout 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.