Interactive guide on the requestIdleCallback() Web API

February 11, 2024 - 6 min read

The Web API requestIdleCallback() is undoubtedly one of the most underrated APIs in the web development ecosystem, offering the capability to schedule non-critical tasks for execution during idle periods in the browser.

Syntax

The requestIdleCallback() method is globally accessible in the browser context and requires one mandatory callback function to run during idle periods as the first argument, with an optional options object as the second argument.

The general syntax in JavaScript is as follows:

requestIdleCallback((deadlineDetails) => {
    // Your low priority task code to execute during idle period
})

Syntax with the timeout option in JavaScript:

requestIdleCallback((deadlineDetails) => {
    // Your low priority task code to execute during idle period
}, { timeout: <some_time_in_milliseconds> })

Syntax in TypeScript:

requestIdleCallback((deadlineDetails: IdleDeadline) => {
    // Your low priority task code to execute during idle period
})
  • The first argument is a callback function invoked by the browser during idle periods in the main thread. The callback receives an IdleDeadline interface object, containing the timeRemaining() method to check the time left for executing low priority task code and the didTimeout attribute to determine if the optional timeout has elapsed.
  • The second argument is an options object used to provide additional information. Presently, the only supported attribute in the options object is timeout, ensuring the callback is invoked before the specified timeout in milliseconds elapses. For instance, if the timeout is set to 3000 milliseconds and the callback hasn't been called after this duration, it takes priority and executes on the next available frame of the main thread. However, it's essential to note that while this timeout feature is beneficial, it could potentially lead to user experience issues such as stutter or lag if the user is actively interacting with the browser during this time.
  • The function returns an ID that can be used to cancel the callback if necessary, utilizing the cancelIdleCallback() function. To cancel the callback, pass the ID as an argument to the cancelIdleCallback() function.

Timeline of requestIdleCallback() Execution by the Browser

Understanding when the browser calls requestIdleCallback() provides insight into its usage.

As previously stated, the callback function passed to the requestIdleCallback() Web API executes during idle periods of the browser.

But Wait! What is an Idle Period in the Browser?

An idle period in the browser encompasses times when the user is not actively interacting with browser interfaces, such as not scrolling, minimizing the browser window, or being on another tab, as well as intervals between active animations.

In essence, it refers to periods when the browser's main thread is not occupied with high-priority tasks.

Thus, the requestIdleCallback() Web API can schedule low-priority tasks that do not require user attention but should execute when the browser's main thread is free.

Consider an example during two frames of an active animation. Let's assume that the browser is executing heavy, low-priority code during the second frame.

Below is an interactive demo for the animation example. Toggle the "requestIdleCallback() turned OFF" button to see how the API can help.

Frame 1 Starts

A Task Process
Render Process
Idle Time

Frame 2 Starts

User click process
Heavy non critical function process
Render Process
Idle Time

Without requestIdleCallback() function

Without utilizing the requestIdleCallback() function, user interactions may feel laggy as heavy, low-priority tasks are executed before the rendering process at the end of the second frame.

Using the requestIdleCallback() function allows us to schedule low-priority tasks during idle periods of the two frames. However, idle periods are not guaranteed for every frame in the browser. There may be scenarios where no idle periods occur, and the low-priority code is not scheduled for some time.

Employing the requestIdleCallback() API enables us to process tasks without introducing jank to the user interface, thereby enhancing the overall user experience.

Example: Sending User Analytics to the Server

An easy-to-understand example is sending user-related analytics to the server.

Since analytics are low-priority tasks that we can afford to run during the idle periods of the browser, the requestIdleCallback() Web API is a good candidate for this scenario.

Let's say we have a function called sendEvents({eventName: "mouse-click", id: "687212ed" }) that sends event information to the server for the currently logged-in user.

Since this is a low-priority task in our case, let's invoke the sendEvents() inside the callback function of the requestIdleCallback() Web API like this:

requestIdleCallback(() => {
	sendEvents({
		eventName: 'mouse-click',
		userId: '687212ed',
	});
});

This function will be invoked by the browser and will send the event details to the server when the browser enters the next idle period.

However, there might be a chance to lag the user experience as we are not checking the allotted or the available idle period to execute our task.

To check exactly that, we can utilize the details regarding the current idle period time frame passed to the callback function as the first parameter.

The object has IdleInterface as its data type.

In the object, we can make use of the timeRemaining() method that returns the time allotted or the remaining time to execute the task in milliseconds before giving back control to the browser. If there is enough time to execute the task, which essentially means the time is greater than the value of 0, we can schedule another requestIdleCallback() to be run in the next idle time frame of the browser with a timeout option if needed.

The code for the same will look like this:

requestIdleCallback((idlePeriodDetails) => {
	const timeRemaining = idlePeriodDetails.timeRemaining();
	console.log('Time Remaining to execute the task in milliseconds: ', timeRemaining);

	// if enough time, then send events
	if (timeRemaining > 0) {
		sendEvents({
			eventName: 'mouse-click',
			userId: '687212ed',
		});
		return;
	}

	// else schedule another task with
	// `requestIdleCallback()` Web API with timeout
	requestIdleCallback(() => {
		sendEvents({
			eventName: 'mouse-click',
			userId: '687212ed',
		});
	}, { timeout: 1000 });
});

Below is an interactive demo to see the remaining time for the above event task to run.


Time Remaining for the event task to run: 0 ms

Canceling an Already Scheduled Task

To cancel a task that has been previously scheduled with the requestIdleCallback() Web API, we can utilise the cancelIdleCallback() Web API within the global browser context.

  • The function requires the reference id returned by the requestIdleCallback() API as its first argument.

The code appears as follows:

const id = requestIdleCallback(() => {
    // Your task code
});

// Cancels the scheduled task
cancelIdleCallback(id);

This code efficiently cancels the execution of a task previously scheduled with requestIdleCallback().

Conclusion

The requestIdleCallback() is a powerful tool in the web developer's toolkit and offers a wide variety of use cases to schedule low-priority tasks. It must be used with caution and practicality to achieve a better user experience.

References