Przedstawiamy pobieranie w tle

Jake Archibald
Jake Archibald

W 2015 r. wprowadziliśmy synchronizację w tle, która umożliwia opóźnianie pracy przez usługę workera do momentu, gdy użytkownik uzyska połączenie z internetem. Oznacza to, że użytkownik może wpisać wiadomość, kliknąć „Wyślij” i opuścić stronę, wiedząc, że wiadomość zostanie wysłana albo teraz, albo gdy będzie miał połączenie z internetem.

Jest to przydatna funkcja, ale wymaga, aby usługa była aktywna przez cały czas pobierania. Nie stanowi to problemu w przypadku krótkich zadań, takich jak wysyłanie wiadomości, ale jeśli zadanie trwa zbyt długo, przeglądarka zabije service workera. W przeciwnym razie może to stanowić zagrożenie dla prywatności i baterii użytkownika.

Co zrobić, jeśli chcesz pobrać coś, co może zająć dużo czasu, np. film, podcasty lub poziomy w grze? Do tego służy pobieranie w tle.

Pobieranie w tle jest dostępne domyślnie od wersji 74 przeglądarki Chrome.

Oto 2-minutowy film demonstrujący tradycyjny stan rzeczy w porównaniu z wykorzystaniem funkcji pobierania w tle:

Wypróbuj wersję demonstracyjnąprzejrzyj kod.

Jak to działa

Pobranie w tle działa w ten sposób:

  1. Informujesz przeglądarkę, aby wykonała grupę pobierania w tle.
  2. Przeglądarka pobiera te elementy, wyświetlając użytkownikowi postęp.
  3. Gdy pobieranie się zakończy (lub zakończy się niepowodzeniem), przeglądarka otwiera Twoje narzędzie związane z usługami i wywołuje zdarzenie, aby poinformować Cię o wyniku. Tutaj decydujesz, co zrobić z otrzymanymi odpowiedziami.

Jeśli użytkownik zamknie strony Twojej witryny po kroku 1, nic się nie stanie – pobieranie będzie kontynuowane. Ponieważ pobieranie jest dobrze widoczne i łatwo je przerwać, nie ma obaw o prywatność w przypadku zbyt długiego zadania synchronizacji w tle. Ponieważ usługa w tle nie działa stale, nie ma obaw, że będzie nadużywać systemu, np. wydobywając bitcoiny w tle.

Na niektórych platformach (np. Androidzie) przeglądarka może się zamknąć po kroku 1, ponieważ może przekazać pobieranie do systemu operacyjnego.

Jeśli użytkownik rozpocznie pobieranie w trybie offline lub utraci połączenie w trakcie pobierania, pobieranie w tle zostanie wstrzymane i wznowione później.

Interfejs API

Wykrywanie funkcji

Podobnie jak w przypadku każdej nowej funkcji, musisz sprawdzić, czy przeglądarka ją obsługuje. Pobranie w tle działa tak:

if ('BackgroundFetchManager' in self) {
  // This browser supports Background Fetch!
}

Uruchamianie pobierania w tle

Główny interfejs API jest zależny od rejestracji skryptu service worker, więc upewnij się, że najpierw zarejestrowałeś skrypt service worker. Następnie:

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,
  });
});

Funkcja backgroundFetch.fetch przyjmuje 3 argumenty:

Parametry
id string
identyfikuje w wyjątkowy sposób to pobieranie w tle.

backgroundFetch.fetch zostanie odrzucony, jeśli identyfikator pasuje do identyfikatora z dotychczasowego pobierania w tle.

requests Array<Request|string>
Elementy do pobrania. Ciągi znaków będą traktowane jako adresy URL i przekształcane w Request za pomocą funkcji new Request(theString).

Możesz pobierać dane z innych źródeł, o ile pozwalają na to te zasoby za pomocą CORS.

Uwaga: Chrome nie obsługuje obecnie żądań, które wymagają wstępnej weryfikacji CORS.

options Obiekt, który może zawierać:
options.title string
Tytuł, który przeglądarka ma wyświetlać wraz z postępem.
options.icons Array<IconDefinition>
Tablica obiektów z atrybutami „src”, „size” i „type”.
options.downloadTotal number
Łączny rozmiar treści odpowiedzi (po rozpakowaniu).

Chociaż jest to opcjonalne, zdecydowanie zalecamy podanie tej informacji. Jest ona używana do informowania użytkownika o rozmiarze pliku do pobrania i o postępie pobierania. Jeśli tego nie zrobisz, przeglądarka poinformuje użytkownika, że rozmiar jest nieznany, przez co może on szybciej przerwać pobieranie.

Jeśli pobrania w tle przekroczą podany tu limit, zostaną przerwane. Nie ma znaczenia, czy pobieranie jest mniejsze niż downloadTotal. Jeśli nie masz pewności, ile będzie wynosić łączny rozmiar pobierania, lepiej zachować ostrożność.

backgroundFetch.fetch zwraca obietnicę, która zwraca wartość BackgroundFetchRegistration. Szczegóły tej funkcji omówię później. Obietnica jest odrzucana, jeśli użytkownik zrezygnował z pobierania lub jeden z podanych parametrów jest nieprawidłowy.

Przesyłanie wielu żądań w ramach pojedynczego pobierania w tle umożliwia łączenie elementów, które z logicznego punktu widzenia są dla użytkownika jedną rzeczą. Na przykład film może być podzielony na tysiące zasobów (co jest typowe dla MPEG-DASH) i zawierać dodatkowe zasoby, takie jak obrazy. Poszczególne poziomy gry mogą być rozmieszczone w wielu zasobach JavaScript, obrazów i dźwięku. Ale dla użytkownika to tylko „film” lub „poziom”.

Pobieranie istniejącego pliku w tle

Możesz uzyskać istniejące pobieranie w tle w ten sposób:

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.get('my-fetch');
});

…przekazując identyfikator pobierania w tle, którego chcesz użyć. Funkcja get zwraca undefined, jeśli nie ma aktywnego pobierania w tle z tym identyfikatorem.

Pobieranie w tle jest uważane za „aktywne” od momentu zarejestrowania do momentu, gdy zakończy się powodzeniem, zakończy się niepowodzeniem lub zostanie przerwane.

Listę wszystkich aktywnych pobierania w tle możesz uzyskać za pomocą getIds:

navigator.serviceWorker.ready.then(async (swReg) => {
  const ids = await swReg.backgroundFetch.getIds();
});

Rejestracje pobierania w tle

Element BackgroundFetchRegistration (bgFetch w przypadkach powyżej) ma:

Właściwości
id string
Identyfikator pobierania w tle.
uploadTotal number
Liczba bajtów do wysłania na serwer.
uploaded number
Liczba bajtów wysłanych pomyślnie.
downloadTotal number
Wartość podana podczas rejestrowania pobierania w tle lub 0.
downloaded number
Liczba bajtów, które zostały odebrane.

Ta wartość może się zmniejszyć. Jeśli na przykład połączenie zostanie utracone i pobieranie nie może zostać wznowione, przeglądarka rozpocznie pobieranie tego zasobu od początku.

result

Jedna z tych wartości:

  • "" – aktywne jest pobieranie w tle, więc nie ma jeszcze wyników.
  • "success" – udało się pobrać dane w tle.
  • "failure" – nie udało się pobrać danych w tle. Ta wartość pojawia się tylko wtedy, gdy pobieranie w tle całkowicie się nie powiedzie, ponieważ przeglądarka nie może wtedy ponownie spróbować ani wznowić pobierania.
failureReason

Jedna z tych wartości:

  • "" – pobieranie w tle się nie zakończyło niepowodzeniem.
  • "aborted" – pobieranie w tle zostało przerwane przez użytkownika lub wywołano funkcję abort().
  • "bad-status" – jedna z odpowiedzi miała stan nieprawidłowy, np. 404.
  • "fetch-error" – jedno z pobierania zakończyło się niepowodzeniem z innego powodu, np. CORS, MIX, nieprawidłowej częściowej odpowiedzi lub ogólnej awarii sieci, której nie można powtórzyć.
  • "quota-exceeded" – podczas pobierania w tle osiągnięto limit miejsca na dane.
  • "download-total-exceeded" – podana wartość parametru „downloadTotal” została przekroczona.
recordsAvailable boolean
Czy można uzyskać dostęp do żądań i odpowiedzi?

Gdy to pole ma wartość Fałsz, nie można użyć wartości match ani matchAll.

Metody
abort() Zwraca Promise<boolean>
Anuluj pobieranie w tle.

Zwrócona obietnica zwraca wartość true, jeśli pobranie zostało przerwane.

matchAll(request, opts) Zwraca Promise<Array<BackgroundFetchRecord>>
Get the requests and responses.

Argumenty są takie same jak w interfejsie API pamięci podręcznej. Wywołanie bez argumentów zwraca obietnicę dla wszystkich rekordów.

Więcej informacji znajdziesz poniżej.

match(request, opts) Zwraca Promise<BackgroundFetchRecord>
Jak wyżej, ale rozwiązuje z pierwszym dopasowaniem.
Wydarzenia
progress Wywoływany, gdy zmieni się wartość właściwości uploaded, downloaded, result lub failureReason.

Śledzenie postępów

Można to zrobić za pomocą zdarzenia progress. Pamiętaj, że downloadTotal to dowolna wartość, którą podano, lub 0, jeśli nie podano wartości.

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}%`);
});

Pobieranie żądań i odpowiedzi

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 to BackgroundFetchRecord, który wygląda tak:

Właściwości
request Request
Prośba, która została przesłana.
responseReady Promise<Response>
Odebrana odpowiedź.

Odpowiedź jest opóźniona, ponieważ może jeszcze nie zostać otrzymana. Jeśli pobranie się nie powiedzie, obietnica zostanie odrzucona.

Zdarzenia skryptu service worker

Wydarzenia
backgroundfetchsuccess Wszystkie dane zostały pobrane.
backgroundfetchfailure Co najmniej jedno z pobierania zakończyło się niepowodzeniem.
backgroundfetchabort Co najmniej 1 pobieranie zakończyło się niepowodzeniem.

Jest to przydatne tylko wtedy, gdy chcesz oczyścić powiązane dane.

backgroundfetchclick Użytkownik kliknął interfejs użytkownika pokazujący postęp pobierania.

Obiekty zdarzeń mają:

Właściwości
registration BackgroundFetchRegistration
Metody
updateUI({ title, icons }) Umożliwia zmianę początkowo ustawionego tytułu lub ikon. Jest to opcjonalne, ale w razie potrzeby pozwala dodać więcej kontekstu. Możesz to zrobić tylko *raz* podczas wydarzeń backgroundfetchsuccess i backgroundfetchfailure.

Odpowiedź na powodzenie lub niepowodzenie

Zdarzenie progress jest już znane, ale jest przydatne tylko wtedy, gdy użytkownik ma otwartą stronę w Twojej witrynie. Główną zaletą pobierania w tle jest to, że aplikacja działa nadal, gdy użytkownik opuszcza stronę lub zamyka przeglądarkę.

Jeśli pobieranie w tle zostanie ukończone, usługa robocza otrzyma zdarzenie backgroundfetchsuccess, a event.registration będzie rejestracją pobierania w tle.

Po tym zdarzeniu pobrane żądania i odpowiedzi nie są już dostępne, więc jeśli chcesz je zachować, przełóż je w inne miejsce, np. do interfejsu API pamięci podręcznej.

Podobnie jak w przypadku większości zdarzeń usługi, użyj funkcji event.waitUntil, aby usługa wiedziała, kiedy zdarzenie się zakończyło.

Na przykład w usługach:

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!' });
  }());
});

Niepowodzenie może być spowodowane pojedynczym błędem 404, który może nie być dla Ciebie ważny, dlatego warto skopiować niektóre odpowiedzi do pamięci podręcznej w sposób opisany powyżej.

Reakcja na kliknięcie

Interfejs wyświetlający postęp i wynik pobierania jest klikalny. Zdarzenie backgroundfetchclick w usługach w tle pozwala na reakcję na to zdarzenie. Jak wspomniano powyżej, event.registration będzie rejestracją pobierania w tle.

Zwykle w przypadku tego zdarzenia otwiera się okno:

addEventListener('backgroundfetchclick', (event) => {
  const bgFetch = event.registration;

  if (bgFetch.result === 'success') {
    clients.openWindow('/latest-podcasts');
  } else {
    clients.openWindow('/download-progress');
  }
});

Dodatkowe materiały

Poprawka: w poprzedniej wersji tego artykułu nieprawidłowo określono pobieranie w tle jako „standard sieci”. Interfejs API nie jest obecnie standardem, ale specyfikację można znaleźć w WICG jako raport Draft Community Group.