Вы на правильном пути, используя clone()
, но время важно.Вы должны убедиться, что вы вызываете clone()
до того, как будет выполнен финальный return response
, потому что в этот момент ответ будет передан на страницу клиента работника сервиса, и его тело будет "использовано".
Есть два способа исправить это: либо вызвать clone()
перед выполнением асинхронного кода кэширования, либо, альтернативно, отложить ваш оператор return response
до завершения кэширования.
Я собираюсь предложитьпервый подход, так как это означает, что вы получите ответ на страницу как можно скорее.Я также собираюсь предложить вам переписать свой код для использования async
/ await
, так как он гораздо более читабелен (и поддерживается любым браузером, который также поддерживает сервисных работников сегодня).
addEventListener("fetch", function(e) {
e.respondWith((async function() {
const cachedResponse = await caches.match(e.request);
if (cachedResponse) {
return cachedResponse;
}
const networkResponse = await fetch(e.request);
const hosts = [
'https://fonts.googleapis.com',
'https://maxcdn.bootstrapcdn.com',
'https://cdnjs.cloudflare.com',
];
if (hosts.some((host) => e.request.url.startsWith(host))) {
// This clone() happens before `return networkResponse`
const clonedResponse = networkResponse.clone();
e.waitUntil((async function() {
const cache = await caches.open(CACHE_NAME);
// This will be called after `return networkResponse`
// so make sure you already have the clone!
await cache.put(e.request, clonedResponse);
})());
}
return networkResponse;
})());
});
Примечание. Синтаксис (async function() {})()
может показаться немного странным, но это ярлык для использования async
/ await
внутри сразу выполняющейся функции, которая будет возвращать обещание.См. http://2ality.com/2016/10/async-function-tips.html#immediately-invoked-async-function-expressions
. Для получения исходного кода необходимо клонировать ответ перед выполнением асинхронного обновления кэша:
var clonedResponse = response.clone();
caches.open(CACHE_NAME).then(function(cache) {
cache.put(e.request, clonedResponse);
});
Учебник по Service Worker от Google имеет пример кода, показывающий правильный путь.В коде есть комментарий с «важной» заметкой, но он просто подчеркивает клон, а не проблему, с которой вы сталкиваетесь при клонировании:
// IMPORTANT: Clone the response. A response is a stream
// and because we want the browser to consume the response
// as well as the cache consuming the response, we need
// to clone it so we have two streams.
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;