Использование Service Worker

В данной статье содержится информация о начале работы с сервис-воркерами, включая базовую архитектуру, процесс регистрации, а также установку и активацию новых сервис-воркеров, обновление существующих сервис-воркеров, управление кешем и настраиваемые ответы и все это в контексте простого приложения с offline-функциональностью.

Предпосылки появления Service Workers

Одной из важнейших проблем, от которой страдали пользователи веб-приложений — это работа в условиях потери связи. Лучшее в мире веб-приложение обеспечит ужасный пользовательский опыт, если вы не можете его загрузить. Предпринималось много попыток создания технологий, которые бы решили эту проблему, и если верить страницам нашего Форума, некоторые из вопросов были решены. Но все же наиважнейшей проблемой является то, что до сих пор отсутствует хороший механизм управления кешем ресурсов и результатов сетевых запросов.

Предыдущей попыткой была технология AppCache, казавшаяся хорошей идеей, потому как позволяла действительно просто указать ресурсы для кеширования. Однако, эта технология допускает много предположений о том, что вы пытаетесь сделать и затем с грохотом ломается, когда ваше приложение работает не в точности с этими предположениями. Чтобы получить больше информации по этой теме, предлагаю прочитать статью Джейка Арчибальда Application Cache is a Douchebag.

На заметку: Начиная с Firefox 44, когда используется AppCache для предоставления странице поддержки offline-режима, в консоли разработчика отображается предупреждение о том, что нужно использовать технологию  Service Workers (баг 1204581.)

Технология Service Workers должна в итоге решить озвученые выше вопросы. Синтаксис Service Worker более сложен, чем тот же AppCache, но компромиссом является то, что вы можете, посредством JavaScript, контролировать AppCache-подразумеваемое поведение с высокой степенью детализации, что позволяет вам решать проблемы и еще много другого. Используя Service Worker, вы можете без труда получить приложение использующее в первую очередь кешированные ресурсы, предоставляя тем самым поведение по-умолчанию в автономном режиме, до того как будет получено по сети больше данных (такой подход называется Offline First). Так обычно работают нативные приложения, что часто является причиной выбора пользователя в их пользу.

Что нужно настроить, чтобы поиграть с Service Worker

Многие функции Service Worker теперь включены по умолчанию в новых браузерах, поддерживающих эту технологию. Однако, если вы обнаружите, что демонстрационный код не работает в вашей версии браузера, вам может понадобиться их включить:

  • Firefox Nightly: Перейдите в раздел about:config и установите параметр dom.serviceWorkers.enabled в значение true; затем перезапустите браузер.
  • Chrome Canary: Перейдите в раздел chrome://flags и включите experimental-web-platform-features; перезапустите браузер (заметьте, что некоторые функции теперь включены по умолчанию в браузере Chrome.)
  • Opera: Перейдите в раздел opera://flags и включите Support for ServiceWorker; перезапустите браузер.

Также вам необходимо предоставлять ваш код по протоколу HTTPS — Service Worker требует это в целях безопасности. Поэтому Github является хорошим местом для экспериментов, потому как он поддерживает протокол HTTPS по умолчанию.

Базовая архитектура

Чтобы сделать базовую настройку Service Worker, как правило, нужно пройти следующие шаги:

  1. URL сервис-воркера извлекается и регистрируется посредством вызова метода ServiceWorkerContainer.register().
  2. Если регистрация прошла успешно, то сервис-воркер начинает работать внутри ServiceWorkerGlobalScope; это основной особый вид контекста, работающий вне главного потока браузера, без доступа к DOM.
  3. Сейчас сервис-воркер уже может обрабатывать события.
  4. Установка сервис-воркера начинается после того, как все контролируемые им страницы закешированы и доступны для последующего использования. Событие install всегда посылается первым воркеру (это может быть использовано для запуска процесса заполнения IndexedDB  и кеширования ресурсов). Эта процедура похожа на то, как устанавливаются нативные или FirefoxOS-приложения — сделать всё доступным в оффлайн-режиме.
  5. Когда обработчик события oninstall завершит свою работу, сервис-воркер считается установленным.
  6. Далее следует активация. После того, как воркер установлен, он получает событие activate. Основное назначение события onactivate — очистка ресурсов, использованных в предыдущей версии скрипта сервис-воркера.
  7. Сервис-воркер теперь может контролировать страницы, но только в случае, если те открыты после успешного вызова register(), то есть документ может начать жизнь с сервис-воркером или даже без него и продолжать нормально работать. Поэтому, документы должны быть перезагружены, чтобы действительно быть подконтрольнымы сервис-воркером.

Следующий рисунок кратко показывает доступные события Service Worker:

install, activate, message, fetch, sync, push

Промисы (обещания)

Промисы — это замечательнейший механизм для запуска асинхронных операций, которые могут успешно зависеть друг от друга. Промисы имеют большое значение в работе сервис-воркеров.

Промисы могут делать много всего, но сейчас вам достаточно знать, что если что-то вернуло промис, вы можете прицепить к нему с конца вызов метода .then(), и передать ему коллбек на случай успеха, неудачи и т. д., или можете в конце добавить вызов .catch(), если хотите подключить фейл-коллбек.

Давайте сравним структуру традиционного синхронного обратного вызова с его промис-эквивалентом.

sync

try {
  var value = myFunction();
  console.log(value);
} catch(err) {
  console.log(err);
}

async

myFunction().then(function(value) {
  console.log(value);
}).catch(function(err) {
  console.log(err);
});

В первом примере код, идущий за вызовом функции myFunction(), будет ждать завершения вызова и возврата значения. Во втором примере  myFunction() возвращает промис для value, в этом случае, последующий код сможет выполняться не дожидаясь завершения основной работы функции. Когда промис разрешится, код, переданный методу then, будет выполнен асинхронно.

А сейчас реальный пример — что, если мы захотели загружать изображения динамически, к тому же мы хотим удостовериться, что изображения загрузились до того, как они будут показаны? То, что мы хотим сделать является стандартной задачей, но она все же может доставить головной боли. Мы можем использовать .onload, чтобы только показать изображение после загрузки, но что с событиями, которые могут произойти до того как мы их начнем слушать? Мы могли бы тогда использовать .complete, но это все еще не надежно, да и что тогда с повторяющимися изображениями? И к тому же это все работает синхронно, блокируя главный поток.
Вместо этого мы можем построить собственный промис, чтобы обработать наш случай. ( смотрите исходный код нашего примера Promises test, или демо)

Примечание: Реальные реализации сервис-воркеров скорее всего будут использовать onfetch а не устаревающий XMLHttpRequest API. Эти возможности не используются здесь, так что можете сосредоточиться на изучении промисов.
function imgLoad(url) {
  return new Promise(function(resolve, reject) {
    var request = new XMLHttpRequest();
    request.open('GET', url);
    request.responseType = 'blob';
    request.onload = function() {
      if (request.status == 200) {
        resolve(request.response);
      } else {
        reject(Error('Image didn\'t load successfully; error code:' + request.statusText));
      }
    };
    request.onerror = function() {
      reject(Error('There was a network error.'));
    };
    request.send();
  });
}

Мы возвращаем новый промис, созданный конструктором Promise(), который, в качестве аргумента, принимает функцию с параметрами resolve и reject. Где-то внутри функции мы должны определить случаи, при которых промис должен  быть разрешен или отклонён — в нашем случае, в зависимости от того, вернулся ли статус 200 ОК или нет — будут вызваны resolve в случае успеха или reject при неудаче. Последующее содержимое этой функции — вполне стандартное XHR-наполнение, поэтому на данный момент не стоит о нем волноваться.

Когда наступает время вызвать  функцию imgLoad(), мы это делаем, передав в качестве параметра url изображения для загрузки, как можно было и ожидать, но последующий код немного отличается:

var body = document.querySelector('body');
var myImage = new Image();
imgLoad('myLittleVader.jpg').then(function(response) {
  var imageURL = window.URL.createObjectURL(response);
  myImage.src = imageURL;
  body.appendChild(myImage);
}, function(Error) {
  console.log(Error);
});

В конце вызова функции, мы «цепляем» к ней вызов промис-метда then(), которому, в качестве параметров, передаем две функции — первая будет вызвана в случае разрешения промиса, созданного вызовом функции imgLoad(), вторая функция будет вызвана в случае отклонения этого промиса. В случае разрешения, мы показываем изображение, url которого передан в качестве аргумента методу imgLoad(), и добавляем его в тело документа в тег body (url для элемента изображения генерируется на основании блоб-объекта response), в случае отклонения промиса в консоли будет отображено сообщение об ошибке.

Всё это происходит асинхронно.

На заметку: Вы можете также объединять вызов нескольких промис-методов в одну цепочку, как в этом примере:
myPromise().then(success, failure).then(success).catch(failure);

На заметку: Вы можете получить гораздо больше информации о промисах, прочитав превосходную статью Джейка Арчибальда (Jake Archibald’s) JavaScript Promises: there and back again.

Демонстрация Service Workers

Чтобы продемонстрировать только базовые моменты регистрации и установки сервис-воркеров, мы создали простое демо-приложение, названое sw-test. Это простая галерея изображений «Star wars Lego». Оно использует промис-функции, чтобы прочитать из JSON-объекта и загрузить, используя технологию Ajax, изображения, находящиеся далее нижнего края страницы, до того как они будут показаны. В приложении также еще регистрируется, устанавливается и активируется сервис-воркер, и, в случае, если браузер поддерживает спецификацию Service Worker, запрашиваемые ресурсы будут закешированы, и приложение будет работать в offline-режиме!

Вы можете посмотреть исходный код на GitHub, а также этот живой пример. Единственное, что мы тут рассмотрим, это промис (смотри app.js строки 22-47), которыя является измененной версией того, что вы прочитали выше в разделе Тестовая демонстрация промисов. Разница в следующем:

  1. Ранее мы передавали параметром лишь URL изображения, которое мы хотели загрузить. Теперь же, мы передаем JSON-фрагмент, содержащий все данные для изображения (смотри как это выглядит в image-list.js). Это сделано потому, что все данные для разрешения каждого промиса должны быть переданы ему, так как он выполняется асинхронно. Если же вы передали лишь URL, и затем чуть позже попытались получить доступ к другим аттрибутам в JSON-фрагменте внутри цикла for(), это бы не сработало, так как этот промис не был бы разрешен во время текущей итерации цикла (это синхронный процесс).
  2. Теперь мы разрешаем промис с параметром-массивом, так как, ниже в коде, мы хотим сделать загруженные данные изображения доступными для разрешающей функции, так же как и имя файла, данные авторства и замещающий текст. (смотри app.js строки 31-34). Промисы будут разрешаться с всего одним аргументом, поэтому, если вы хотите разрешать их с множеством параметров, вы должны использовать массив/объект.
  3. Чтобы получить доступ к решенным значениям промисов, мы обращаемся к ним так, как предполагалось (смотри app.js строки 60-64). По началу это может выглядеть немного странно, но именно так и работают промисы.

Погружение в Service Worker

Итак, переходим к Service Worker!

Регистрация воркеров

Ниже представлен первый блок кода файла app.js. Это точка входа в Service Worker.

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw-test/sw.js', { scope: '/sw-test/' }).then(function(reg) {
    // регистрация сработала
    console.log('Registration succeeded. Scope is ' + reg.scope);
  }).catch(function(error) {
    // регистрация прошла неудачно
    console.log('Registration failed with ' + error);
  });
};
  1. Внешний условный блок выполняет проверку на поддержку Service Worker, чтобы убедиться что технология доступна, до того как начать регистрацию.
  2. Далее, чтобы зарегистрировать сервис-воркера для этого сайта, мы используем функцию ServiceWorkerContainer.register(). Сервис-воркер представляет собой JavaScript-файл приложения (обратите внимание, что URL указывается относительно «корня», а не места расположения JS-файла, регистрирующего сервис-воркера).
  3. Параметр scope — не обязателен, он может быть использован для указания подмножества контента, которое вы хотите отдать под контроль сервис-воркера. В нашем случае, мы указали ‘/sw-test/'. Если вы не укажете его, то будет использовано значение по умолчанию; мы же указали его только в целях илюстрации.
  4. Метод .then() был использован для обработки успешной регистрации. Если промис разрешится успешно, то код, переданный этому методу, будет выполнен.
  5. Ну и наконец, в конец нашего промиса мы добавляем функцию .catch() , которая будет выполнена в случае, если промис будет отклонён.

Предыдущий код регистрирует сервис-воркера, который работает в worker-контексте, и следовательно, не имеет доступа к DOM. Затем вы запускате код в сервис-воркере, вне ваших страниц, чтобы контролировать их загрузку.

Один сервис-воркер может контролировать несколько страниц. Каждый раз, когда загружается страница, находящаяся в пределах области видимости, сервис-воркер будет установлен на ней и начнет работу. Поэтому будьте осторожны с применением глобальных переменных в скриптах сервис-воркеров, потому как каждая страница не имеет свой уникальный экземпляр сервис-воркера.

На заметку: Сервис-воркеры функционально похожи на прокси-серверы, они позволяют модифицировать запросы и ответы, замещая записями из собственного кеша, и многое другое.

На заметку: Есть одна очень хорошая особенность при работе с сервис-воркерами. В случае, если вы используете функционал проверки поддержки Service Worker, то приложение в браузерах, не имеющих поддержки, продолжат нормально работать с ожидаемым поведением. Кроме того, если браузер поддерживает только AppCache, то будет использована эта технология. В случае, если браузер поддерживает и AppCache и Service Worker, то будет использована Service Worker.

Почему мой сервис-воркер не прошел регистрацию?

Это может произойти по следующим причинам:

  1. Приложение загружено не по протоколу HTTPS.
  2. Путь к сервис-воркеру указан некорректно — он должен быть написан относительно origin запроса, не относительно корневой директории приложения. В нашем примере запрос к файлу воркера выглядит как https://mdn.github.io/sw-test/sw.js и запрос к корневой папке приложения вглядит так https://mdn.github.io/sw-test/. Но если мы укажем в качестве пути к сервис-воркеру /sw.js, то запрос для его загрузки получится такой https://mdn.github.io/sw.js, поэтому нужно указывать /sw-test/sw.js, а не /sw.js.
  3. Origin сервис-воркера отличается от origin вашего приложения. Это также не разрешается.

Также обратите внимание:

  • В сервис-воркер будут попадать только те запросы, которые соответствуют его scope.
  • Максимальная видимость scope сервис-воркера равна его location.
  • Если ваш сервис-воркер работает на клиенте, которому был передан заголовок Service-Worker-Allowed, вы можете указать список максимальных scope’ов для этих воркеров.

Установка и активация: заполнение кеша

После того как ваш сервис-воркер будет зарегистрирован, браузер может попробовать установить его и активировать на странице/сайте.

Событие install возникает после того как установка успешно завершится. Это событие используется главным образом для того, чтобы заполнить кеш браузера ресурсами, необходимыми для успешного запуска в offline-режиме. Для этого используется новый API хранилища Service Worker — cache — глобальный для всех сервис-воркеров, который позволяет нам хранить результаты запросов, используя в качестве ключа для их получения сами запросы. Этот API работает аналогично стандартному кешу браузера, но только для вашего домена. Данные в кеше сохраняются до тех пор, пока вы сами не решите их удалить — вы имеете полный контроль.

На заметку: Cache API поддерживается не всеми браузерами (смотрите раздел Browser support чтобы получить больше информации). Если вы хотите сейчас использовать эту технологию, то можете рассмотреть возможность использования полифила, который доступен в Google’s Topeka demo, или можете хранить ресурсы в IndexedDB.

Давайте начнем этот раздел посмотрев на фрагмент кода ниже — это первый блок кода, который вы увидите в нашем сервис-воркере:

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',
        '/sw-test/star-wars-logo.jpg',
        '/sw-test/gallery/',
        '/sw-test/gallery/bountyHunters.jpg',
        '/sw-test/gallery/myLittleVader.jpg',
        '/sw-test/gallery/snowTroopers.jpg'
      ]);
    })
  );
});
  1. Здесь к сервис-воркеру ( this ) мы добаляем обработчик события install, внутри которого вызывается метод   ExtendableEvent.waitUntil() объекта события. Эта конструкция гарантирует, что сервис-воркер не будет установлен, если код, переданый в нее, не завершится успешно.
  2. Внутри waitUntil() мы используем метод  caches.open() чтобы создать новый кеш, который назовём v1; это будет первая версия кеша ресурсов. Этот метод возвращает промис для созданого кеша; когда он выполнится, у объекта созданного  кеша мы  вызовем метод addAll(), который в качестве параметра ожидает получить массив origin-относительных URL всех ресурсов, которые мы хотим хранить в кеше.
  3. Если промис будет отклонен, то установка будет завершена неудачно и воркер ничего не сделает. Это хорошо, потому как вы можете исправить свой код и затем попробовать провести регистрацию в следующий раз.
  4. После успешной установки сервис-воркер активируется. Этот момент не очень важен при первоначальной установке/активации сервис-воркера, в то же время он имеет большое значение, когда происходит обновление воркера (смотрите раздел Обновление ваших сервис-воркеров, находящийся ниже).

На заметку: localStorage работает похожим образом, но в синхронном режиме, чего не разрешается в сервис-воркерах.

На заметку: Если вам это необходимо, то  в сервис-воркерах вы можете использовать  IndexedDB

Настраиваемые ответы на запросы

Теперь ресурсы вашего сайта находятся в кеше и вам необходимо указать сервис-воркеру, что делать с  этим контентом. Это легко сделать, обработав событие fetch.

Событие fetch возникает каждый раз, когда запрашиваются любые подконтрольные сервис-воркеру ресурсы, к которым относятся документы из области видимости и другие ресурсы, связанные с этими документами (например, если в index.html происходит крос-доменный запрос для загрузки изображения, то он тоже попадет в сервис-воркер).

Вы можете подключить к сервис-воркеру обработчик события fetch и внутри него на объекте события вызвать метод respondWith(), чтобы заменить ответы и показать собственную «магию».

this.addEventListener('fetch', function(event) {
  event.respondWith(
    // магия происходит здесь
  );
});

Для начала, на каждый сетевой запрос мы можем отдать в ответ ресурс, чей url соответствует  запросу:

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request);
  );
});

caches.match(event.request) позволяет нам проверять сетевой запрос ресурса на соответствие какому-либо доступному в кеше ресурсу, если такой ресурс имеется. Соответствие проверяется по url и изменяемым заголовкам.

Давайте рассмотрим несколько других вариантов реализации нашей магии (чтобы получить больше информации об интерфейсах Request и Response смотрите документацию к Fetch API.)

  1. Конструктор Response() позволяет вам создавать собственные ответы. В данном случае, мы всего лишь возвращаем простую текстовую строку:
    new Response('Hello from your friendly neighbourhood service worker!');
  2. В этом более сложном объекте Response показано, как вы можете передать набор заголовков в свой ответ, эмулируя стандартный HTTP-ответ. Здесь мы просто сообщаем браузеру, чем является содержимое ответа:
    new Response('<p>Hello from your friendly neighbourhood service worker!</p>', {
      headers: { 'Content-Type': 'text/html' }
    })
  3. Если совпадение не было найдено в кеше, вы можете попросить браузер загрузить тот же ресурс, чтобы получить новый файл через обычную сеть, если она доступна:
    fetch(event.request)
  4. Если информация, соответствующая запросу, в кеше не найдена, а также сеть не доступна, то вы можете просто ответить на запрос какой-либо страницей по умолчанию, которая хранится в кеше, используя  match():
    caches.match('/fallback.html');
  5. Вы можете получить больше информации о каждом запросе, используя для этого свойства объекта Request, который можно получить как свойство объекта FetchEvent:
    event.request.url
    event.request.method
    event.request.headers
    event.request.body

Восстановление неудачных запросов

Итак, caches.match(event.request) отработает как нужно только в том случае, если в кеше сервис-воркера будет найдено соответствие запросу. Но что произойдет, если такого соответствия не будет найдено? Если мы не предоставим никакого механизма обработки такой ситуации, то промис  разрешится со значением undefined и мы не получим никакого значения.

К счастью, сервис-воркеры имеют структуру основанную на промисах, что делает тривиальной такую обработку и предоставляет большое количество способов успешно обработать запрос:

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response) {
      return response || fetch(event.request);
    })
  );
});

Если промис будет отклонен, функция catch() вернет обычный сетевой запрос к внешнему ресурсу. Это значит, что, если сеть доступна, то ресурс просто загрузится с сервера.

Если же мы были достаточно умны, то мы не стали бы просто возвращать сетевой запрос, а сохранили бы его результат в кеше, чтобы иметь возможность получить его в offline-режиме. В случае с нашим демо-приложением «Star Wars gallery», это означает, что, если в галерею будет добавлено еще одно изображение, то оно будет получено и сохранено в кеше:

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(resp) {
      return resp || fetch(event.request).then(function(response) {
        return caches.open('v1').then(function(cache) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

Здесь мы возвращаем обычный сетевой запрос, который возвращен вызовом fetch(event.request); этот запрос также является промисом. Когда промис разрешится, мы получим кеш вызвав caches.open('v1'); этот метод также возвращает промис. Когда разрешится уже второй промис, будет использован вызов cache.put(),чтобы поместить ресурс в кеш. Ресурс получен через event.request, а ответ через клонирование response.clone(). Клон помещается в кеш, а оригинальный ответ передается браузеру, который передает его странице, которая запросила ресурс.

Почему? Потому, что потоки запроса и ответа могут быть прочитаны только единожды. Чтобы ответ был получен браузером и сохранен в кеше — нам нужно клонировать его. Так, оригинальный объект отправится браузеру, а клон будет закеширован. Оба они будут прочитаны единожды.

У нас все ещё остается единственная проблема — если на какой-либо запрос в кеше не будет найдено соответствие, и в этот момент сеть не доступна, то наш запрос завершится неудачно. Давайте реализуем запасной вариант по умолчанию, при котором пользователь, в описанном случае, будет получать хоть что-нибудь:

this.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(resp) {
      return resp || fetch(event.request).then(function(response) {
        caches.open('v1').then(function(cache) {
          cache.put(event.request, response.clone());
        });
        return response;
      });
    }).catch(function() {
      return caches.match('/sw-test/gallery/myLittleVader.jpg');
    })
  );
});

Здесь мы решили обрабатывать только картинки, потому что единственные запросы, которые могут не удасться — это загрузка новых картинок, так как все остальное было закешировано во время обработки события install, которое мы обсуждали ранее.

Обновленный код шаблона приложения

Здесь используется стандартный механизм сцепления промисов в цепочки, а ответ на запрос возвращается в документ не дожидаясь разрешения  вызова  caches.open():

this.addEventListener('fetch', function(event) {
  var response;
  event.respondWith(caches.match(event.request).then(function() {
    return fetch(event.request);
  }).then(function(r) {
    response = r;
    caches.open('v1').then(function(cache) {
      cache.put(event.request, response);
    });
    return response.clone();
  }).catch(function() {
    return caches.match('/sw-test/gallery/myLittleVader.jpg');
  }));
});

Обновление ваших сервис-воркеров

Если после того как ваш сервис-воркер был установлен, а затем стала доступна новая его версия, при обновлении или загрузке страницы новая версия будет установлена в фоновом режиме, но не будет активирована. Она будет активирована лишь когда не останется ни одной страницы, использующей старую версию сервис-воркера.

Вы можете обновить обработчик события install в новой версии сервис-воркера, чтобы получить примерно такое (обратите внимание на номер новой версии):

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v2').then(function(cache) {
      return cache.addAll([
        '/sw-test/',
        '/sw-test/index.html',
        '/sw-test/style.css',
        '/sw-test/app.js',
        '/sw-test/image-list.js',// включение других ресурсов для новой версии...
      ]);
    });
  );
});

В то время, как этот код работает, предыдущая версия также доступна. Новая версия устанавливается в фоновом режиме. Мы можем работать с версией кеша v2, в то время как версия v1 не будет разрушена.

Когда ни одна страница не будет использовать текущую версию, новый воркер активируется и станет ответственным за обработку всех запросов.

Удаление старого кеша

Вы можете обрабатывать событие activate. Оно обычно используется в случае, если нужно выполнить такие действия, которые бы нарушили работу воркеров предыдущей версии, если они все еще работают со старым кешем. Также это событие полезно использовать для того, чтобы удалить ненужные данные, чтобы освободить место, занимаемое на диске, потому что каждый браузер имеет жесткие ограничения на размер хранилища кеша, которое доступно для использования сервис-воркерами.

Promise переданный в waitUntil() заблокирует другие события до своего завершения, поэтому можно быть уверенным, что процесс очистки закончится раньше, чем выполнится первое событие fetch на новом кеше.

this.addEventListener('activate', function(event) {
  var cacheWhitelist = ['v2'];
  event.waitUntil(
    caches.keys().then(function(keyList) {
      return Promise.all(keyList.map(function(key) {
        if (cacheWhitelist.indexOf(key) === -1) {
          return caches.delete(key);
        }
      }));
    })
  );
});

Инструменты разработчика

В браузере Chrome есть раздел chrome://inspect/#service-workers, где можно увидеть активность сервис-воркеров и их хранилища. Также есть раздел chrome://serviceworker-internals, в котором можно получить более детальную информацию, запустить, остановить и отладить процессы сервис-воркеров. В будущем будет реализована поддержка эмуляции различного качества сетевых соединений, а также их отсутствие

Firefox то же начал реализовывать полезные инструменты, связанные с сервис-воркерами:

  • Вы можете пройти в раздел about:serviceworkers, чтобы посмотреть зарегистрированные сервис-воркеры и обновить или удалить их.
  • Во время тестирования вы можете снять ограничение HTTPS, поставив флажок «Enable Service Workers over HTTP (when toolbox is open)» в разделе настроек Firefox Devtools.

Спецификации

Спецификация Статус Коментарии
Service Workers Рабочий черновик Initial definition.

Совместимость с браузерами

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari (WebKit)
Basic support 40.0 33.0 (33.0)[1] Нет 24 Нет

[1] Service workers (and Push) have been disabled in the Firefox 45 Extended Support Release (ESR.)