Key Takeaways
- JavaScript is a blocking language, meaning it can only run one task at a time, which can lead to unresponsive browsers for long-running scripts. However, less crucial background tasks can be scheduled to run without directly affecting the user experience.
- `requestIdleCallback` is an API that allows for non-essential tasks to be scheduled during idle times in the browser, similar to the function of `requestAnimationFrame`. It can be used with a timeout option to ensure tasks run within a set timeframe, even if the browser isn’t idle.
- `requestIdleCallback` is an experimental feature with limited browser support and is not suitable for tasks with unpredictable execution times or for resolving promises. For unsupported browsers or tasks that require direct access to the DOM, alternatives like Web Workers or setTimeout can be used.
script
tag or has to run a JavaScript function. The code is downloaded (if required) and run immediately before further events or rendering can be handled. This is necessary because your script could do anything: load further code, remove every DOM element, redirect to another URL etc. Even if there were two or more pixies, the others would need to stop work while the first processed your code. That’s blocking. It’s the reason why long-running scripts cause browsers to become unresponsive.
You often want JavaScript to run as soon as possible because the code initializes widgets and event handlers. However, there are less important background tasks which don’t directly affect the user experience, e.g.
- recording analytics data
- sending data to social networks (or adding 57 ‘share’ buttons)
- pre-fetching content
- pre-processing or pre-rendering HTML
setTimeout
, e.g. setTimeout(doSomething, 1);
. The browser will execute the doSomething()
function once other immediately-executing tasks have completed. In effect, it’s put on the bottom of the to-do list. Unfortunately, the function will be called regardless of processing demand.
requestIdleCallback
requestIdleCallback is a new API designed to schedule non-essential background tasks during those moments the browser is taking a breather. It’s reminiscent of requestAnimationFrame which calls a function to update an animation before the next repaint. You can read more aboutrequestAnimationFrame
here: Simple Animations Using requestAnimationFrame
We can detect whether requestIdleCallback
is supported like so:
if ('requestIdleCallback' in window) {
// requestIdleCallback supported
requestIdleCallback(backgroundTask);
}
else {
// no support - do something else
setTimeout(backgroundTask1, 1);
setTimeout(backgroundTask2, 1);
setTimeout(backgroundTask3, 1);
}
You can also specify an options object parameter with a timeout (in milliseconds), e.g.
requestIdleCallback(backgroundTask, { timeout: 3000; });
This ensures your function is called within the first three seconds, regardless of whether the browser is idle.
requestIdleCallback
calls your function once only and passes a deadline
object with the following properties:
didTimeout
— set true if the optional timeout firedtimeRemaining()
— a function which returns the number of milliseconds remaining to perform a task
timeRemaining()
will allocate no more than 50ms for your task to run. It won’t stop tasks exceeding this limit but, preferably, you should call requestIdleCallback
again to schedule further processing.
Let’s create a simple example which executes several tasks in order. The tasks are stored in an array as function references:
// array of functions to run
var task = [
background1,
background2,
background3
];
if ('requestIdleCallback' in window) {
// requestIdleCallback supported
requestIdleCallback(backgroundTask);
}
else {
// no support - run all tasks soon
while (task.length) {
setTimeout(task.shift(), 1);
}
}
// requestIdleCallback callback function
function backgroundTask(deadline) {
// run next task if possible
while (deadline.timeRemaining() > 0 && task.length > 0) {
task.shift()();
}
// schedule further tasks if necessary
if (task.length > 0) {
requestIdleCallback(backgroundTask);
}
}
Is There Anything That Shouldn’t Be Done In a requestIdleCallback?
As Paul Lewis notes in his blog post on the subject, the work you do in a requestIdleCallback should be in small chunks. It is not suitable for anything with unpredictable execution times (such as manipulating the DOM, which is better done using a requestAnimationFrame callback). You should also be wary of resolving (or rejecting) Promises, as the callbacks will execute immediately after the idle callback has finished, even if there is no more time remaining.requestIdleCallback Browser Support
requestIdleCallback
is an experimental feature and the spec is still in flux, so don’t be surprised when you encounter API changes. It’s supported in Chrome 47 … which should be available before the end of 2015. Opera should also gain the feature imminently. Microsoft and Mozilla are both considering the API and it sounds promising. There’s no word from Apple as usual. If you fancy giving it a whirl today, your best bet is to use Chrome Canary (a much newer release of Chrome that’s not as well tested, but has the latest shiny stuff).
Paul Lewis (mentioned above) created a simple requestIdleCallback shim. This implements the API as described but it’s not a polyfill which can emulate the browser’s idle-detection behavior. It resorts to using setTimeout
like the example above but it’s a good option if you want to use the API without object detection and code forking.
While support is limited today, requestIdleCallback
could be an interesting facility to help you maximize web page performance. But what do you think? I’d be glad to hear your thoughts in the comments section below.
Frequently Asked Questions (FAQs) about Scheduling Background Tasks in JavaScript
What is the Background Tasks API in JavaScript?
The Background Tasks API in JavaScript is a powerful tool that allows developers to schedule tasks to run in the background, even when the main thread is idle. This API is particularly useful for tasks that require heavy computation or network requests, as it allows these tasks to be performed without blocking the main thread and potentially causing a poor user experience. The Background Tasks API is part of the larger Web APIs provided by modern browsers, and it provides a more efficient and performance-friendly way to handle background tasks compared to traditional methods like setTimeout or setInterval.
How does the Background Tasks API differ from setTimeout and setInterval?
The setTimeout and setInterval functions are traditional methods used in JavaScript for scheduling tasks to run after a certain delay or at regular intervals, respectively. However, these methods have some limitations, particularly when it comes to performance. They run on the main thread, which means they can block other tasks and potentially cause a poor user experience if they take too long to complete. On the other hand, the Background Tasks API runs tasks in the background, separate from the main thread. This means it can handle more intensive tasks without affecting the performance of the main thread.
Can I use the Background Tasks API in all browsers?
The Background Tasks API is a relatively new addition to the Web APIs provided by modern browsers, and as such, it may not be supported in all browsers. It’s always a good idea to check the current level of support for any API you plan to use in your projects. Websites like Can I Use provide up-to-date information on the level of support for various APIs across different browsers.
How can I schedule a task to run in the background using the Background Tasks API?
To schedule a task using the Background Tasks API, you can use the requestIdleCallback method. This method takes a callback function as its first argument, which will be executed when the browser is idle. Here’s a basic example:window.requestIdleCallback(() => {
// Your background task goes here
});
How can I cancel a scheduled background task?
If you need to cancel a background task that you’ve scheduled with the requestIdleCallback method, you can use the cancelIdleCallback method. This method takes the ID returned by requestIdleCallback as its argument. Here’s an example:const id = window.requestIdleCallback(() => {
// Your background task goes here
});
// Later, if you need to cancel the task
window.cancelIdleCallback(id);
What is the use of the timeout option in requestIdleCallback?
The timeout option in requestIdleCallback is used to specify a maximum time in milliseconds that the browser should wait before running the callback, even if it’s not idle. This can be useful if your background task needs to be run within a certain timeframe.
How can I handle errors in a background task?
Handling errors in a background task scheduled with the Background Tasks API is similar to handling errors in any other JavaScript code. You can use a try/catch block to catch any errors that occur during the execution of your task. Here’s an example:window.requestIdleCallback(() => {
try {
// Your background task goes here
} catch (error) {
console.error('An error occurred in the background task', error);
}
});
Can I use the Background Tasks API in Node.js?
The Background Tasks API is a part of the Web APIs provided by modern browsers, and as such, it’s not available in Node.js by default. However, there are other ways to run background tasks in Node.js, such as using worker threads or child processes.
Can I use the Background Tasks API with Promises or async/await?
The Background Tasks API does not return a Promise, so it can’t be used directly with async/await. However, you can wrap the requestIdleCallback method in a Promise if you need to use it in an asynchronous context. Here’s an example:const runInBackground = (task) => {
return new Promise((resolve) => {
window.requestIdleCallback(() => {
const result = task();
resolve(result);
});
});
};
// You can now use runInBackground with async/await
const result = await runInBackground(() => {
// Your background task goes here
});
What are some use cases for the Background Tasks API?
The Background Tasks API is useful for any tasks that require heavy computation or network requests, as it allows these tasks to be performed without blocking the main thread. Some examples of use cases might include fetching and processing data from an API, performing complex calculations, or updating the UI based on user interactions.
Craig is a freelance UK web consultant who built his first page for IE2.0 in 1995. Since that time he's been advocating standards, accessibility, and best-practice HTML5 techniques. He's created enterprise specifications, websites and online applications for companies and organisations including the UK Parliament, the European Parliament, the Department of Energy & Climate Change, Microsoft, and more. He's written more than 1,000 articles for SitePoint and you can find him @craigbuckler.