Tweaks to cache.addAll() and importScripts() coming in Chrome 71
Developers using service workers and the Cache Storage API should be on the lookout for two small changes rolling out in Chrome 71. Both changes bring Chrome's implementation more in line with specifications and other browsers.
Disallowing asynchronous importScripts()
importScripts()
tells your main service worker script to pause its current execution, download additional code from
a given URL, and run it to completion in the current global
scope. Once that's done,
the main service worker script resumes execution. importScripts()
comes in handy when
you want to break your main service worker script into smaller pieces for organizational reasons, or
pull in third-party code to add functionality to your service worker.
Browsers attempt to mitigate the possible performance gotchas of "download and run some synchronous
code" by automatically caching anything pulled in via importScripts()
, meaning that after the
initial download, there's very little overhead involved in executing the imported code.
For that to work, though, the browser needs to know that there won't be any "surprise" code imported
into the service worker after the initial
installation.
As per the service worker specification,
calling importScripts()
is supposed to only work during the synchronous execution of the top-level
service worker script, or if needed, asynchronously inside of the install
handler.
Prior to Chrome 71, calling importScripts()
asynchronously outside of the install
handler would
work. Starting with Chrome 71, those calls
throw a runtime exception (unless the same URL was previously imported in an install
handler),
matching the behavior in other browsers.
Instead of code like this:
// This only works in Chrome 70 and below.
self.addEventListener('fetch', event => {
importScripts('my-fetch-logic.js');
event.respondWith(self.customFetchLogic(event));
});
Your service worker code should look like:
// Move the importScripts() to the top-level scope.
// (Alternatively, import the same URL in the install handler.)
importScripts('my-fetch-logic.js');
self.addEventListener('fetch', event => {
event.respondWith(self.customFetchLogic(event));
});
Note: Some users of the Workbox library might be
implicitly relying on asynchronous calls to importScripts()
without realizing it. Please see this
guidance to
make sure you don't run into issues in Chrome 71.
Deprecating repeated URLs passed to cache.addAll()
If you're using the Cache Storage API alongside of a service worker, there's another small change in
Chrome 71 to align with the relevant
specification. When the same URL is
passed in multiple times to a single call to
cache.addAll()
, the
specification says that the promise returned by the call should reject.
Prior to Chrome 71, that was not detected, and the duplicate URLs would effectively be ignored.
This logging is a prelude to Chrome 72, where instead of just a logged warning, duplicate URLs will
lead to cache.addAll()
rejecting. If you're calling cache.addAll()
as part of a promise chain
passed to
InstallEvent.waitUntil()
,
as is common practice, that rejection might cause your service worker to fail to install.
Here's how you might run into trouble:
const urlsToCache = [
'/index.html',
'/main.css',
'/app.js',
'/index.html' // Oops! This is listed twice and should be removed.
];
self.addEventListener('install', event => {
event.waitUntil(
caches.open('my-cache')
.then(cache => cache.addAll(urlsToCache))
);
});
This restriction only applies to the actual URLs being passed to cache.addAll()
, and caching what
ends up being two equivalent responses that have different URLs—like '/'
and '/index.html'
—will
not trigger a rejection.
Test your service worker implementation widely
Service workers are widely implemented across all major "evergreen" browsers at this point. If you regularly test your progressive web app against a number of browsers, or if you have a significant number of users who don't use Chrome, then chances are you've already detected the inconsistency and updated your code. But on the off chance that you haven't noticed this behavior in other browsers, we wanted to call out the change before switching Chrome's behavior.