By now you have probably heard of RequestAnimationFrame, if not the tldr is that RequestAnimationFrame lets the browser time
animations for you to line up perfectly with the painting process, meaning you get more performant animations. This is a perfect example
letting the platform work for you, and therefore gaining better performance since the browser can be smart about the whole thing. Since your
PWA’s are going to be running mainly in a mobile environment, where computation resources are very limited, the more we can let the
browser help us be fast, the better off we will be. A newer example of an api that lets the browser help us that you may not have heard of yet is
requestIdleCallback. Lets dive into this new api and see how it can help make your next PWA lightning fast!

What exactly is requestIdleCallback?

requestIdleCallback is a new api that lets the browser execute code when it is idle. In this case idle means that the browser is currently
at the end of a frame, has some free time and that the user is not interacting with the app in any way. requestIdleCallback will only fire
the function passed to it when these two requirements are met, which does mean that if it does not have any idle time it may not actually
execute the function passed to it. While this may sound like a bad thing, Hey I wrote this code, execute it damnit , requestIdleCallback
has some handy features that we will see below to let you ensure that a bit of code is executed if it has to be.

Use Cases

Before we go into examples of how to use requestIdleCallback I thought I would introduce some instances where this new api comes in handy.
First, most big sites and PWA’s use analytics of some kind so that the developer of that site can know how their users use this app. Now,
imagine if your on a mobile device (and remember, not everyone has that brand new Google Pixel or Iphone 7), click on a menu and boom,
that analaytics service kicks off some javascript to send the event to the analytics server. What happens when thread blocking JS
runs when the browser is trying to animate? Jank, and sometimes lots of it. Now imagine that the developer used requestIdleCallback to
send that analytics request. With requestIdleCallback the browser would have waited until it was idle (no painting or animating) and then
would have sent the analytics request while it was idle. Now we have a jank free experience and still get to send those analytics! Second,
imagine that we have a PWA that processes audio data using the Web Audio api. On mobile devices this can potentially be heavy on the
cpu and do some thread locking. If we timed this work using requestIdleCallback the browser could then help us do this heavy work while
the browser is idle and keep our PWA silky smooth!

One last thing before we use this api

As with all newer api’s we should always remember to fall back gracefully to older ways of doing things on browsers that do not yet support
certain api’s. Current browser support for requestIdleCallback is as follows:

  • Chrome: Fully supported, available in Chrome since Chrome 47 (current stable version of Chrome is Chrome 55)
  • Firefox: Coming in Firefox 52 (current stable version of Firefox is Firefox 50)
  • Edge: Under consideration
  • Opera: Fully supported as of Opera 42 (current stable version of Opera is Opera 42)
  • Safari: Not supported, unfortunately could not find any info on when Safari plans to support requestIdleCallback

To test for requestIdleCallback suppport in your code and fall back gracefully for browsers with no support you can use this
pattern.

1
2
3
4
5
6
if ('requestIdleCallback' in window) {
requestIdleCallback(doHeavyComputation)
} else {
// No support so lets just execute our code
doHeavyComputation()
}

With this code we will use requestIdleCallback when it is available and simply fall back to just executing our code when
there is no support for it in the browser our app is running on.

Ok, so how do I use this thing?

If you have ever used RequestAnimationFrame then the requestIdleCallback api should feel pretty familiar to you. To use requestIdleCallback
the first step is to pass it a callback. Our callback should be the function that we would like to schedule for idle time, doHeavyComputation in this example.

1
requestIdleCallback(doHeavyComputation);

In a nutshell this is all you really need to use requestIdleCallback, but this usage does not guarantee that your code will be executed
and also does not let you know how much time is actually available for that specific call to requestIdleCallback so lets expand on this
example a little. The function that gets called by requestIdleCallback will receive a deadline object as a paramater. This deadline
object offers some pretty handy information.

1
2
3
4
5
6
7
8
9
10
11
// function were passing to requestIdleCallback
function doHeavyComputation(deadline) {
while (deadline.timeRemaining > 0) {
// do work while we have time
for (let i = 0; i < 20000000; i++) {
amazinglyHugeArray.push({fact: 'JS is cool'});
}
}
// we ran out of time, schedule again
requestIdleCallback(doHeavyComputation)
}

In this example we are using the deadline object to check if we have time left. While we do have free time we will do our heavy computation.
The cool thing here is that if we run out of time doing this work then we can simply schedule another requestIdleCallback and try again next
time the browser is idle. Now, say we want to ensure that some code will be ran even if requestIdleCallback runs out of time. requestIdleCallback
takes a second paramater which lets us set a timeout. This forces the browser to finish executing our code by the time the timeout is reached. Now
before I even show an example of this I would like to stress that you should only do this if you really really need to. The whole point
of using requestIdleCallback is to schedule execution of code while the browser is idle so that we do not jank our PWA and it is definitely
possible that when the timeout is reached the browser may still be busy, which means that executing our code may jank the world. So now that
you are thoroughly warned about releasing the jank monster lets see an example of timeout in action.

1
2
3
4
5
6
7
8
9
10
requestIdleCallback(doHeavyComputation, { timeout: 3000 });
function doHeavyComputation(deadline) {
while (deadline.timeRemaining > 0 || deadline.didTimeout) {
// do work while we have time
for (let i = 0; i < 20000000; i++) {
amazinglyHugeArray.push({fact: 'JS is cool'});
}
}
}

As you can see here, we are now also checking for deadline.didTimeout. didTimeout will be true when our timeout has been reached which means
that even though the browser has ran out of idle time we are still going to execute our code. Again, this can release the jank monster so be careful.

What does this have to do with my PWA ?

One of the big “selling points” of PWA’s is that they are able to be very light on resources and give an awesome experience on even the lowest of low end devices.
The problem here is even if we have done everything else that a good PWA should do perf wise (fast load time, cache resources for even faster load time next load etc etc),
if we execute javascript while the app is animating then we give our user a janky, slow experience, and who likes jank and slowness right? By taking advantage of perf oriented
API’s like like requestIdleCallback we can let the platform our PWA’s all run on, the browser, help us succeed in our perf goals!

Hope you enjoyed this post! Make sure to tune in next time to learn about devices api’s and why they are important for PWA’s!