В 2015 году мы внедрили функцию фоновой синхронизации , которая позволяет сервисному работнику откладывать выполнение задач до появления у пользователя подключения к интернету. Это означает, что пользователь может ввести сообщение, нажать «Отправить» и покинуть сайт, зная, что сообщение будет отправлено либо сейчас, либо когда появится подключение.
Это полезная функция, но она требует, чтобы сервис-воркер был активен на протяжении всего времени загрузки данных. Это не проблема для коротких задач, таких как отправка сообщения, но если задача занимает слишком много времени, браузер завершит работу сервис-воркера, в противном случае это создаёт риск для конфиденциальности пользователя и разрядки батареи.
Итак, что делать, если вам нужно загрузить что-то, что может занять много времени, например, фильм, подкаст или уровни игры? Для этого и предназначена функция «Фоновая загрузка» .
Функция фоновой загрузки доступна по умолчанию, начиная с Chrome 74.
Вот короткая двухминутная демонстрация, демонстрирующая традиционное положение дел по сравнению с использованием Background Fetch:
Как это работает
Фоновая выборка работает следующим образом:
- Вы сообщаете браузеру о необходимости выполнить группу выборок в фоновом режиме.
- Браузер извлекает эти данные и отображает ход выполнения для пользователя.
- После завершения или сбоя выборки браузер открывает ваш сервис-воркер и генерирует событие, сообщающее о произошедшем. Здесь вы решаете, что делать с ответами, если делать вообще.
Если пользователь закроет страницы вашего сайта после шага 1, ничего страшного, загрузка продолжится. Поскольку загрузка хорошо заметна и её легко прервать, не возникает проблем с конфиденциальностью, связанных со слишком долгой фоновой синхронизацией. Поскольку сервис-воркер не работает постоянно, не возникает опасений, что он может нарушить работу системы, например, майнить биткоины в фоновом режиме.
На некоторых платформах (например, Android) браузер может закрыться после шага 1, поскольку браузер может передать загрузку операционной системе.
Если пользователь начнет загрузку, находясь в автономном режиме, или отключится от сети во время загрузки, фоновая загрузка будет приостановлена и возобновлена позже.
API
Обнаружение особенностей
Как и с любой новой функцией, вам нужно проверить, поддерживает ли её браузер. Для функции «Фоновая загрузка» всё просто:
if ('BackgroundFetchManager' in self) {
// This browser supports Background Fetch!
}
Запуск фоновой выборки
Основной API зависит от регистрации сервис-воркера , поэтому сначала убедитесь, что вы зарегистрировали сервис-воркер. Затем:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.fetch('my-fetch', ['/ep-5.mp3', 'ep-5-artwork.jpg'], {
title: 'Episode 5: Interesting things.',
icons: [{
sizes: '300x300',
src: '/ep-5-icon.png',
type: 'image/png',
}],
downloadTotal: 60 * 1024 * 1024,
});
});
backgroundFetch.fetch
принимает три аргумента:
Параметры | |
---|---|
id | string однозначно идентифицирует эту фоновый запрос. |
requests | Array< Request |string> Что нужно извлечь. Строки будут обрабатываться как URL-адреса и преобразовываться в Request с помощью new Request(theString) .Вы можете получать данные из других источников, если ресурсы позволяют это сделать через CORS . Примечание: в настоящее время Chrome не поддерживает запросы, требующие предварительной проверки CORS. |
options | Объект, который может включать в себя следующее: |
options.title | string Заголовок, который будет отображаться в браузере вместе с ходом выполнения. |
options.icons | Array< IconDefinition > Массив объектов с `src`, `size` и `type`. |
options.downloadTotal | number Общий размер тел ответов (после распаковки с помощью gzip). Хотя это необязательно, настоятельно рекомендуется предоставить его. Он используется для того, чтобы сообщить пользователю объём загрузки и предоставить информацию о ходе процесса. Если вы не предоставите этот параметр, браузер сообщит пользователю, что размер неизвестен, и в результате пользователь, скорее всего, прервёт загрузку. Если количество фоновых загрузок превысит указанное здесь значение, загрузка будет прервана. Совершенно нормально, если загрузка меньше значения |
backgroundFetch.fetch
возвращает обещание, которое разрешается с помощью BackgroundFetchRegistration
. Подробнее об этом я расскажу позже. Промо отклоняется, если пользователь отказался от загрузок или один из предоставленных параметров недействителен.
Предоставление множества запросов на одну фоновую загрузку позволяет объединить элементы, которые для пользователя логически представляют собой единое целое. Например, фильм может быть разбит на тысячи ресурсов (типично для MPEG-DASH ) и содержать дополнительные ресурсы, такие как изображения. Уровень игры может быть распределен по множеству ресурсов JavaScript, изображений и аудио. Но для пользователя это просто «фильм» или «уровень».
Получение существующей фоновой выборки
Вы можете получить существующую фоновую выборку следующим образом:
navigator.serviceWorker.ready.then(async (swReg) => {
const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});
…передавая идентификатор нужной фоновой выборки. get
возвращает undefined
если нет активной фоновой выборки с таким идентификатором.
Фоновая выборка считается «активной» с момента ее регистрации и до тех пор, пока она не завершится успешно, не завершится неудачей или не будет прервана.
Список всех активных фоновых выборок можно получить с помощью getIds
:
navigator.serviceWorker.ready.then(async (swReg) => {
const ids = await swReg.backgroundFetch.getIds();
});
Фоновые регистрации выборки
BackgroundFetchRegistration
( bgFetch
в приведенных выше примерах) имеет следующее:
Характеристики | |
---|---|
id | string Идентификатор фоновой выборки. |
uploadTotal | number Количество байтов, которые необходимо отправить на сервер. |
uploaded | number Количество успешно отправленных байтов. |
downloadTotal | number Значение, предоставленное при регистрации фоновой выборки, или ноль. |
downloaded | number Количество успешно полученных байтов. Это значение может уменьшиться. Например, если соединение разорвано и загрузка не может быть возобновлена, в этом случае браузер перезапускает загрузку этого ресурса с нуля. |
result | Один из следующих вариантов:
|
failureReason | Один из следующих вариантов:
|
recordsAvailable | boolean Можно ли получить доступ к базовым запросам/ответам? Если это значение ложно, |
Методы | |
abort() | Возвращает Promise<boolean> Отменить фоновую загрузку. Возвращенное обещание разрешается со значением true, если выборка была успешно прервана. |
matchAll(request, opts) | Возвращает Promise<Array<BackgroundFetchRecord>> Получайте запросы и ответы. Аргументы здесь те же, что и у API кэша . Вызов без аргументов возвращает обещание для всех записей. Более подробную информацию смотрите ниже. |
match(request, opts) | Возвращает Promise<BackgroundFetchRecord> Как и выше, но решается с первым совпадением. |
События | |
progress | Срабатывает при изменении любого из параметров uploaded , downloaded , result или failureReason . |
Отслеживание прогресса
Это можно сделать через событие progress
. Помните, что downloadTotal
— это указанное вами значение или 0
, если значение не указано.
bgFetch.addEventListener('progress', () => {
// If we didn't provide a total, we can't provide a %.
if (!bgFetch.downloadTotal) return;
const percent = Math.round(bgFetch.downloaded / bgFetch.downloadTotal * 100);
console.log(`Download progress: ${percent}%`);
});
Получение запросов и ответов
bgFetch.match('/ep-5.mp3').then(async (record) => {
if (!record) {
console.log('No record found');
return;
}
console.log(`Here's the request`, record.request);
const response = await record.responseReady;
console.log(`And here's the response`, response);
});
record
— это BackgroundFetchRecord
, и она выглядит так:
Характеристики | |
---|---|
request | Request Запрос был предоставлен. |
responseReady | Promise<Response> Полученный ответ. Ответ находится за обещанием, поскольку он мог ещё не быть получен. Промис будет отклонён, если запрос не удастся. |
События для работников сферы услуг
События | |
---|---|
backgroundfetchsuccess | Все было успешно получено. |
backgroundfetchfailure | Одна или несколько попыток загрузки не удались. |
backgroundfetchabort | Одна или несколько попыток загрузки не удались. Это действительно полезно только в том случае, если вы хотите выполнить очистку связанных данных. |
backgroundfetchclick | Пользователь нажал на пользовательский интерфейс хода загрузки. |
Объекты событий имеют следующее:
Характеристики | |
---|---|
registration | BackgroundFetchRegistration |
Методы | |
updateUI({ title, icons }) | Позволяет изменить изначально заданные заголовок/значки. Это необязательно, но позволяет предоставить больше контекста при необходимости. Это можно сделать только *один раз* во время событий backgroundfetchsuccess и backgroundfetchfailure . |
Реакция на успех/неудачу
Мы уже видели событие progress
, но оно полезно только пока у пользователя открыта страница вашего сайта. Главное преимущество фоновой загрузки заключается в том, что она продолжает работать даже после того, как пользователь покидает страницу или даже закрывает браузер.
Если фоновая выборка успешно завершена, ваш сервис-воркер получит событие backgroundfetchsuccess
, а event.registration
станет регистрацией фоновой выборки.
После этого события извлеченные запросы и ответы больше не будут доступны, поэтому, если вы хотите их сохранить, переместите их куда-нибудь, например в API кэша .
Как и в случае большинства событий Service Worker, используйте event.waitUntil
, чтобы Service Worker знал, когда событие завершится.
Например, в вашем сервисном работнике:
addEventListener('backgroundfetchsuccess', (event) => {
const bgFetch = event.registration;
event.waitUntil(async function() {
// Create/open a cache.
const cache = await caches.open('downloads');
// Get all the records.
const records = await bgFetch.matchAll();
// Copy each request/response across.
const promises = records.map(async (record) => {
const response = await record.responseReady;
await cache.put(record.request, response);
});
// Wait for the copying to complete.
await Promise.all(promises);
// Update the progress notification.
event.updateUI({ title: 'Episode 5 ready to listen!' });
}());
});
Сбой мог свестись к одной ошибке 404, которая могла быть для вас не важна, поэтому, возможно, все равно стоит скопировать некоторые ответы в кэш, как указано выше.
Реакция на щелчок
Интерфейс, отображающий ход загрузки и результат, можно нажимать. Событие backgroundfetchclick
в сервис-воркере позволяет реагировать на это. Как и выше, event.registration
будет регистрацией фоновой загрузки.
Обычно в этом случае открывают окно:
addEventListener('backgroundfetchclick', (event) => {
const bgFetch = event.registration;
if (bgFetch.result === 'success') {
clients.openWindow('/latest-podcasts');
} else {
clients.openWindow('/download-progress');
}
});
Дополнительные ресурсы
Исправление: В предыдущей версии этой статьи Background Fetch ошибочно назывался «веб-стандартом». В настоящее время API не входит в число стандартов, спецификация представлена в WICG в виде черновика отчёта группы сообщества.