Skip to content

Commit

Permalink
Using service workers: call waitUntil on cache promises (#38149)
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh-Cena authored Feb 16, 2025
1 parent 98228d8 commit 2fdcb82
Showing 1 changed file with 16 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -204,26 +204,26 @@ const putInCache = async (request, response) => {
await cache.put(request, response);
};

const cacheFirst = async (request) => {
const cacheFirst = async (request, event) => {
const responseFromCache = await caches.match(request);
if (responseFromCache) {
return responseFromCache;
}
const responseFromNetwork = await fetch(request);
putInCache(request, responseFromNetwork.clone());
event.waitUntil(putInCache(request, responseFromNetwork.clone()));
return responseFromNetwork;
};

self.addEventListener("fetch", (event) => {
event.respondWith(cacheFirst(event.request));
event.respondWith(cacheFirst(event.request, event));
});
```

If the request URL is not available in the cache, we request the resource from the network request with `await fetch(request)`. After that, we put a clone of the response into the cache. The `putInCache()` function uses `caches.open('v1')` and `cache.put()` to add the resource to the cache. The original response is returned to the browser to be given to the page that called it.

Cloning the response is necessary because request and response streams can only be read once. In order to return the response to the browser and put it in the cache we have to clone it. So the original gets returned to the browser and the clone gets sent to the cache. They are each read once.

What might look a bit weird is that the promise returned by `putInCache()` is not awaited. But the reason is that we don't want to wait until the response clone has been added to the cache before returning a response.
What might look a bit weird is that the promise returned by `putInCache()` is not awaited. The reason is that we don't want to wait until the response clone has been added to the cache before returning a response. However, we do need to call `event.waitUntil()` on the promise, to make sure the service worker doesn't terminate before the cache is populated.

The only trouble we have now is that if the request doesn't match anything in the cache, and the network is not available, our request will still fail. Let's provide a default fallback so that whatever happens, the user will at least get something:

Expand All @@ -233,7 +233,7 @@ const putInCache = async (request, response) => {
await cache.put(request, response);
};

const cacheFirst = async ({ request, fallbackUrl }) => {
const cacheFirst = async ({ request, fallbackUrl, event }) => {
// First try to get the resource from the cache
const responseFromCache = await caches.match(request);
if (responseFromCache) {
Expand All @@ -246,7 +246,7 @@ const cacheFirst = async ({ request, fallbackUrl }) => {
// response may be used only once
// we need to save clone to put one copy in cache
// and serve second one
putInCache(request, responseFromNetwork.clone());
event.waitUntil(putInCache(request, responseFromNetwork.clone()));
return responseFromNetwork;
} catch (error) {
const fallbackResponse = await caches.match(fallbackUrl);
Expand All @@ -268,6 +268,7 @@ self.addEventListener("fetch", (event) => {
cacheFirst({
request: event.request,
fallbackUrl: "/gallery/myLittleVader.jpg",
event,
}),
);
});
Expand Down Expand Up @@ -308,7 +309,12 @@ const putInCache = async (request, response) => {
await cache.put(request, response);
};

const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
const cacheFirst = async ({
request,
preloadResponsePromise,
fallbackUrl,
event,
}) => {
// First try to get the resource from the cache
const responseFromCache = await caches.match(request);
if (responseFromCache) {
Expand All @@ -319,7 +325,7 @@ const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
const preloadResponse = await preloadResponsePromise;
if (preloadResponse) {
console.info("using preload response", preloadResponse);
putInCache(request, preloadResponse.clone());
event.waitUntil(putInCache(request, preloadResponse.clone()));
return preloadResponse;
}

Expand All @@ -329,7 +335,7 @@ const cacheFirst = async ({ request, preloadResponsePromise, fallbackUrl }) => {
// response may be used only once
// we need to save clone to put one copy in cache
// and serve second one
putInCache(request, responseFromNetwork.clone());
event.waitUntil(putInCache(request, responseFromNetwork.clone()));
return responseFromNetwork;
} catch (error) {
const fallbackResponse = await caches.match(fallbackUrl);
Expand Down Expand Up @@ -379,6 +385,7 @@ self.addEventListener("fetch", (event) => {
request: event.request,
preloadResponsePromise: event.preloadResponse,
fallbackUrl: "/gallery/myLittleVader.jpg",
event,
}),
);
});
Expand Down

0 comments on commit 2fdcb82

Please sign in to comment.