developer.chrome.com Open in urlscan Pro
2a00:1450:400d:804::2011  Public Scan

URL: https://developer.chrome.com/blog/background-fetch/
Submission: On February 14 via manual from IL — Scanned from DE

Form analysis 0 forms found in the DOM

Text Content

Skip to content



Home

Docs

Blog

Articles

Home

Docs

Blog

Articles
Blog


INTRODUCING BACKGROUND FETCH

Published on Monday, December 3, 2018 • Updated on Thursday, July 28, 2022

Jake Archibald

Human boy working on web standards at Google

Website Twitter
Table of contents
 * How it works
 * The API
   * Feature detect
   * Starting a background fetch
   * Getting an existing background fetch
   * Background fetch registrations
   * Tracking progress
   * Getting the requests and responses
   * Service worker events
   * Reacting to success/failure
   * Reacting to click
 * Additional resources

In 2015 we introduced Background Sync which allows the service worker to defer
work until the user has connectivity. This means the user could type a message,
hit send, and leave the site knowing that the message will be sent either now,
or when they have connectivity.

It's a useful feature, but it requires the service worker to be alive for the
duration of the fetch. That isn't a problem for short bits of work like sending
a message, but if the task takes too long the browser will kill the service
worker, otherwise it's a risk to the user's privacy and battery.

So, what if you need to download something that might take a long time, like a
movie, podcasts, or levels of a game. That's what Background Fetch is for.

Background Fetch is available by default since Chrome 74.

Here's a quick two minute demo showing the traditional state of things, vs using
Background Fetch:



Try the demo yourself and browse the code.


# HOW IT WORKS

A background fetch works like this:

 1. You tell the browser to perform a group of fetches in the background.
 2. The browser fetches those things, displaying progress to the user.
 3. Once the fetch has completed or failed, the browser opens your service
    worker and fires an event to tell you what happened. This is where you
    decide what to do with the responses, if anything.

If the user closes pages to your site after step 1, that's ok, the download will
continue. Because the fetch is highly visible and easily abortable, there isn't
the privacy concern of a way-too-long background sync task. Because the service
worker isn't constantly running, there isn't the concern that it could abuse the
system, such as mining bitcoin in the background.

On some platforms (such as Android) it's possible for the browser to close after
step 1, as the browser can hand off the fetching to the operating system.

If the user starts the download while offline, or goes offline during the
download, the background fetch will be paused and resumed later.


# THE API


# FEATURE DETECT

As with any new feature, you want to detect if the browser supports it. For
Background Fetch, it's as simple as:

if ('BackgroundFetchManager' in self) {
  // This browser supports Background Fetch!
}


# STARTING A BACKGROUND FETCH

The main API hangs off a service worker registration, so make sure you've
registered a service worker first. Then:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
    title: 'Episode 5: Interesting things.',
    icons: [{
      sizes: '300x300',
      src: '/ep-5-icon.png',
      type: 'image/png',
    }],
    downloadTotal: 60 * 1024 * 1024,
  });
});

Many examples in this article use async functions. If you aren't familiar with
them, check out the guide.

backgroundFetch.fetch takes three arguments:

Parametersidstring
uniquely identifies this background fetch.

backgroundFetch.fetch will reject if the ID matches an existing background
fetch.

requestsArray<Request|string>
The things to fetch. Strings will be treated as URLs, and turned into Requests
via new Request(theString).

You can fetch things from other origins as long as the resources allow it via
CORS.

Note: Chrome doesn't currently support requests that would require a CORS
preflight.

optionsAn object which may include the following:options.titlestring
A title for the browser to display along with
progress.options.iconsArray<IconDefinition>
An array of objects with a `src`, `size`, and `type`.options.downloadTotalnumber
The total size of the response bodies (after being un-gzipped).

Although this is optional, it's strongly recommended that you provide it. It's
used to tell the user how big the download is, and to provide progress
information. If you don't provide this, the browser will tell the user the size
is unknown, and as a result the user may be more likely to abort the download.

If the background fetch downloads exceeds the number given here, it will be
aborted. It's totally fine if the download is smaller than the downloadTotal, so
if you aren't sure what the download total will be, it's best to err on the side
of caution.

backgroundFetch.fetch returns a promise that resolves with a
BackgroundFetchRegistration. I'll cover the details of that later. The promise
rejects if the user has opted out of downloads, or one of the provided
parameters is invalid.

Providing many requests for a single background fetch lets you combine things
that are logically a single thing to the user. For example, a movie may be split
into 1000s of resources (typical with MPEG-DASH), and come with additional
resources like images. A level of a game could be spread across many JavaScript,
image, and audio resources. But to the user, it's just "the movie", or "the
level".

Caution

Caution: Chrome's implementation currently only accepts requests without a body.
In future, bodies will be allowed, meaning you can use background fetch for
large uploads, such as photos and video.


# GETTING AN EXISTING BACKGROUND FETCH

You can get an existing background fetch like this:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});

…by passing the id of the background fetch you want. get returns undefined if
there's no active background fetch with that ID.

A background fetch is considered "active" from the moment it's registered, until
it either succeeds, fails, or is aborted.

You can get a list of all the active background fetches using getIds:

navigator.serviceWorker.ready.then(async (swReg) => {
  const ids = await swReg.backgroundFetch.getIds();
});


# BACKGROUND FETCH REGISTRATIONS

A BackgroundFetchRegistration (bgFetch in the above examples) has the following:

Propertiesidstring
The background fetch's ID.uploadTotalnumber
The number of bytes to be sent to the server.uploadednumber
The number of bytes successfully sent.downloadTotalnumber
The value provided when the background fetch was registered, or
zero.downloadednumber
The number of bytes successfully received.

This value may decrease. For example, if the connection drops and the download
cannot be resumed, in which case the browser restarts the fetch for that
resource from scratch.

result

One of the following:

 * "" - The background fetch is active, so there's no result yet.
 * "success" - The background fetch was successful.
 * "failure" - The background fetch failed. This value only appears when the
   background fetch totally fails, as in the browser cannot retry/resume.

failureReason

One of the following:

 * "" - The background fetch hasn't failed.
 * "aborted" – The background fetch was aborted by the user, or abort() was
   called.
 * "bad-status" - One of the responses had a not-ok status, e.g. 404.
 * "fetch-error" - One of the fetches failed for some other reason, e.g. CORS,
   MIX, an invalid partial response, or a general network failure for a fetch
   that cannot be retried.
 * "quota-exceeded" - Storage quota was reached during the background fetch.
 * "download-total-exceeded" - The provided `downloadTotal` was exceeded.

recordsAvailableboolean
Can the underlying requests/responses can be accessed?

Once this is false match and matchAll cannot be used.

Methodsabort()Returns Promise<boolean>
Abort the background fetch.

The returned promise resolves with true if the fetch was successfully aborted.

matchAll(request, opts)Returns Promise<Array<BackgroundFetchRecord>>
Get the requests and responses.

The arguments here are the same as the cache API. Calling without arguments
returns a promise for all records.

See below for more details.

match(request, opts)Returns Promise<BackgroundFetchRecord>
As above, but resolves with the first match.EventsprogressFired when any of
uploaded, downloaded, result, or failureReason change.


# TRACKING PROGRESS

This can be done via the progress event. Remember that downloadTotal is whatever
value you provided, or 0 if you didn't provide a value.

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
  console.log(`Download progress: ${percent}%`);
});


# GETTING THE REQUESTS AND RESPONSES

Caution

In Chrome's current implementation you can only get the requests and responses
during backgroundfetchsuccess, backgroundfetchfailure, and backgroundfetchabort
service worker events (see below). In future you'll be able to get in-progress
fetches.

bgFetch.match('/ep-5.mp3').then(async (record) => {
  if (!record) {
    console.log('No record found');
    return;
  }

  console.log(`Here's the request`, record.request);
  const response = await record.responseReady;
  console.log(`And here's the response`, response);
});

record is a BackgroundFetchRecord, and it looks like this:

PropertiesrequestRequest
The request that was provided.responseReadyPromise<Response>
The fetched response.

The response is behind a promise because it may not have been received yet. The
promise will reject if the fetch fails.


# SERVICE WORKER EVENTS

EventsbackgroundfetchsuccessEverything was fetched
successfully.backgroundfetchfailureOne or more of the fetches
failed.backgroundfetchabortOne or more fetches failed.

This is only really useful if you want to perform clean-up of related data.

backgroundfetchclickThe user clicked on the download progress UI.

The event objects have the following:

PropertiesregistrationBackgroundFetchRegistrationMethodsupdateUI({ title, icons
})Lets you change the title/icons you initially set. This is optional, but it
lets you provide more context if necessary. You can only do this *once* during
backgroundfetchsuccess and backgroundfetchfailure events.


# REACTING TO SUCCESS/FAILURE

We've already seen the progress event, but that's only useful while the user has
a page open to your site. The main benefit of background fetch is things
continue to work after the user leaves the page, or even closes the browser.

If the background fetch successfully completes, your service worker will receive
the backgroundfetchsuccess event, and event.registration will be the background
fetch registration.

After this event, the fetched requests and responses are no longer accessible,
so if you want to keep them, move them somewhere like the cache API.

As with most service worker events, use event.waitUntil so the service worker
knows when the event is complete.

Note: You can't hold the service worker open indefinitely here, so avoid doing
things that would keep the service worker open a long time here, such as
additional fetching.

For example, in your service worker:

addEventListener('backgroundfetchsuccess', (event) => {
  const bgFetch = event.registration;

  event.waitUntil(async function() {
    // Create/open a cache.
    const cache = await caches.open('downloads');
    // Get all the records.
    const records = await bgFetch.matchAll();
    // Copy each request/response across.
    const promises = records.map(async (record) => {
      const response = await record.responseReady;
      await cache.put(record.request, response);
    });

    // Wait for the copying to complete.
    await Promise.all(promises);

    // Update the progress notification.
    event.updateUI({ title: 'Episode 5 ready to listen!' });
  }());
});

Failure may have come down to a single 404, which may not have been important to
you, so it might still be worth copying some responses into a cache as above.


# REACTING TO CLICK

The UI displaying the download progress and result is clickable. The
backgroundfetchclick event in the service worker lets you react to this. As
above event.registration will be the background fetch registration.

The common thing to do with this event is open a window:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});


# ADDITIONAL RESOURCES

 * Explainer
 * Specification
 * chromestatus.com entry

Correction: A previous version of this article incorrectly referred to
Background Fetch as being a "web standard". The API is not currently on the
standards track, the specification can be found in WICG as a Draft Community
Group Report.

Updated on Thursday, July 28, 2022 • Improve article

Table of contents
 * How it works
 * The API
   * Feature detect
   * Starting a background fetch
   * Getting an existing background fetch
   * Background fetch registrations
   * Tracking progress
   * Getting the requests and responses
   * Service worker events
   * Reacting to success/failure
   * Reacting to click
 * Additional resources

Follow us

Contribute
 * File a bug
 * View source

Related content
 * web.dev
 * Case studies
 * Podcasts

Connect
 * Twitter
 * YouTube
 * GitHub

Chrome Firebase All products Privacy Terms
Choose language ENGLISH (en)
Content available under the CC-BY-SA-4.0 license
We serve cookies on this site to analyze traffic, remember your preferences, and
optimize your experience.
More details Ok, Got it.