Cache Storage: What's the difference between cache.add and cache.put?
If you want to learn everything you need to know about service workers, head over to serviceworkerbook.com and order my book, “Let’s Take This Offline”! Be sure to use cache-add
for 10% off!
What is cache.add?
If you’ve ever looked at a service worker, you’re probably familiar with this little bit of code from an oninstall
function:
cache.addAll([ /* ... */ ]);
Or perhaps you’ve seen:
cache.add(/* ... */);
Fortunately the syntax is pretty self-explanatory on what these two functions are doing. They are adding items to a cache. In one case they add multiple files, and in another, only a single file or request.
What is cache.put
Another function for adding items to a cache is cache.put
. This function has two parameters: the request and the response.
cache.put(request, response);
So besides the obvious difference, that addAll
and add
only accept a single parameter (though of varying types), what is the difference between add
and put
?
The difference between add and put
It’s a trick question! As it turns out, add
and addAll
use put
under the hood.
It works like this:
1. For each request in the array (or a single request in the case of `add`), do a fetch
2. Add the response to a key-value pair list, with the request as the identifying key
3. Cache the request _and_ response together (by using put)
Source: Service Worker Spec
When to use add and when to use put
Usually in a service worker install function, you’ll see this:
// code: https://github.com/carmalou/cache-response-with-sw/blob/master/serviceworker.js
self.oninstall = function() {
caches.open(cacheName).then(function(cache) {
cache.addAll([ /* ... */ ])
.then(/* ... */)
.catch(/* ... */)
})
}
It makes a lot of sense to use addAll
over put
in this case, because the user has never visited our site before. addAll
is going to go fetch those responses and add them to your cache automatically, so we can use it during once our install event and no longer need to go over the network to get our static assets like HTML files. Additionally the syntax is very clean, so its very clear to future-us what we’re doing.
So, if we should use addAll
in an install event, where should we use put
?
Before we get into the add
vs put
debate, let’s look at our fetch event.
self.onfetch = function(event) {
event.respondWith(
caches.match(event.request)
.then(function(cachedFiles) {
if(cachedFiles) {
return cachedFiles;
} else {
// should I use cache.put or cache.add??
}
})
)
)
}
In the above code, we have a fetch event sitting between the client making the request and the network which will process the request. Our service worker will intercept our request and check for a match within our cache. If a match is found, the service worker will return the response. Otherwise, we’ll fall into the else.
Let’s first look at using cache.add
:
/* ... */
if(cachedFiles) {
return cachedFiles;
} else {
return caches.open(cacheName)
.then(function(cache) {
return caches.add(request)
.then(function() {
return fetch(request)
});
})
}
/* ... */
Briefly, should no match be found, we’ll fall into the else. Within the else, we’ll go ahead and open up the cache we want to store our data in, and then use cache.add
to store off the response. At the end, we’ll go ahead and do a fetch over the network so the client can access the data.
But there’s a problem with this: we’ll end up needing to do two fetches! Because caches.add
doesn’t actually return the response to the request it runs, we still need to do an additional fetch to get the response back to the client. Doing two fetches for the same data is redundant, and fortunately put
makes it unnecessary.
Let’s take a look at how we might rewrite this with put
:
// code: https://github.com/carmalou/cache-response-with-sw/blob/master/serviceworker.js
/* ... */
if(cachedFiles) {
return cachedFiles;
} else {
return fetch(event.request)
.then(function(response) {
return caches.open(cacheName)
.then(function(cache) {
return cache.put(event.request, response.clone());
})
.then(function() {
return response;
})
})
}
/* ... */
So we’ve sort of flipped the order of what we were doing before with cache.add
. Instead of doing the fetch last, we go ahead and do it first. After that’s completed, we have a reference to the response. Next we can go ahead and get a reference to our cache with caches.open
and use put
to cache both the request and the response.
Note: Keep your eyes peeled for a blog post about why we are cloning the response!
Once we’ve finished caching the data, we can go ahead and return the response back to the client! And then next time the client makes this request, a trip over the network will no longer be necessary!
So, in conclusion, you should use cache.add
or cache.addAll
in situations where you don’t need that data to get back to the client – such as your install event. When you’re caching new requests, such as API requests, it’s better to use cache.put
because it allows you to cache the data and send it back to the client with a single network request.
Thanks so much for reading! If you liked this post, you should head on over to serviceworkerbook.com and buy my book! Be sure to use cache-add
for 10% off!