Włącz przesyłanie strumieniowe i uzyskuj natychmiastowe odpowiedzi

Każdy, kto używał skryptów service worker, może powiedzieć, że są one asynchroniczne. Wykorzystują one wyłącznie interfejsy oparte na zdarzeniach, np. FetchEvent, i używają obietnic do sygnalizowania zakończenia operacji asynchronicznych.

Asynchroniczność jest równie ważna, ale mniej widoczna dla dewelopera w przypadku odpowiedzi dostarczanych przez moduł obsługi zdarzeń pobierania w skrypcie service worker. Odpowiedzi strumieniowane są tutaj złotym standardem: umożliwiają stronie, która przesłała pierwotne żądanie, rozpoczęcie pracy z odpowiedzią, gdy tylko pierwszy kawałek danych stanie się dostępny, i potencjalnie używanie parserów zoptymalizowanych do strumieniowego przesyłania danych w celu stopniowego wyświetlania treści.

Gdy tworzysz własny moduł obsługi zdarzeń fetch, często przekazujesz metodę respondWith() Response (lub obietnicę typu Response), którą otrzymujesz za pomocą fetch() lub caches.match(), i wywołujesz ją codziennie. Dobra wiadomość jest taka, że obiekty Response utworzone za pomocą obu tych metod są już przesyłane strumieniowo. Zła wiadomość jest taka, że treści Response utworzonych ręcznie nie można odtwarzać strumieniowo, przynajmniej do tej pory. Tutaj pojawia się interfejs Streams API.

Strumienie?

Strumień jest źródłem danych, które można tworzyć i przetwarzać stopniowo. Oferuje interfejs do odczytywania lub zapisywania asynchronicznych fragmentów danych, których tylko podzbiór może być dostępny w pamięci w danym momencie. Na razie interesuje nas ReadableStream, za pomocą którego można utworzyć obiekt Response przekazywany do fetchEvent.respondWith():

self.addEventListener('fetch', event => {
    var stream = new ReadableStream({
    start(controller) {
        if (/* there's more data */) {
        controller.enqueue(/* your data here */);
        } else {
        controller.close();
        }
    });
    });

    var response = new Response(stream, {
    headers: {'content-type': /* your content-type here */}
    });

    event.respondWith(response);
});

Strona, której żądanie wywołało zdarzenie fetch, otrzyma odpowiedź strumieniową od razu po wywołaniu metody event.respondWith(). Będzie kontynuować odczyt z tego strumienia, dopóki skrypt service worker kontynuuje enqueue()pobieranie dodatkowych danych. Odpowiedź przepływająca z skryptu service worker na stronę jest naprawdę asynchroniczna i mamy pełną kontrolę nad wypełnieniem strumienia.

Rzeczywiste zastosowania

W poprzednim przykładzie widać było kilka zastępczych komentarzy /* your data here */, które zawierały mało informacji o implementacji. Jak mógłby wyglądać autentyczny przykład?

Jake Archibald (nie zaskakujący!) poda świetny przykład wykorzystania strumieni do łączenia odpowiedzi HTML z wielu fragmentów kodu HTML w pamięci podręcznej ze strumieniem danych „na żywo” przesyłanych przez fetch() – w tym przypadku są to treści na jego blog.

Zaletą odpowiedzi strumieniowej, jak wyjaśnił Jake, jest to, że przeglądarka może analizować i renderować przesyłany strumieniowo kod HTML, w tym początkowy fragment, który jest szybko wczytywany z pamięci podręcznej, bez konieczności czekania na zakończenie pobierania całej treści bloga. W pełni wykorzystuje to możliwości progresywnego renderowania kodu HTML przeglądarki. Inne zasoby, które można renderować stopniowo, takie jak niektóre formaty obrazów i filmów, również mogą skorzystać na tym podejściu.

Strumienie? czy powłoki aplikacji?

Dotychczasowe sprawdzone metody używania mechanizmów Service Worker w aplikacjach internetowych skupiają się na modelu App Shell + zawartość dynamiczna. Podejście to polega na agresywnym buforowaniu „powłoki” aplikacji internetowej – minimalnej ilości kodu HTML, JavaScript i CSS niezbędnej do wyświetlenia struktury i układu – oraz wczytywania dynamicznej zawartości potrzebnej do poszczególnych stron za pomocą żądania po stronie klienta.

Strumienie oferują alternatywę dla modelu App Shell, w którym pełniejsza odpowiedź HTML jest przesyłana strumieniowo do przeglądarki, gdy użytkownik przechodzi na nową stronę. Odpowiedź przesyłana strumieniowo może korzystać z zasobów zapisanych w pamięci podręcznej, dzięki czemu może szybko dostarczyć początkowy fragment kodu HTML, nawet w trybie offline. W efekcie odpowiedzi wyglądają bardziej jak tradycyjne treści odpowiedzi renderowane przez serwer. Jeśli na przykład Twoja aplikacja internetowa korzysta z systemu zarządzania treścią, który renderuje kod HTML przez serwer, łącząc częściowe szablony, model ten jest bezpośrednio przekształcany w odpowiedzi strumieniowane, gdzie logika szablonów jest replikowana przez skrypt service worker, a nie serwer. Ten film pokazuje, że w takim przypadku korzyści płynące z szybkości przesyłania odpowiedzi strumieniowych mogą być bardzo uderzające:

Jedną z ważnych zalet przesyłania strumieniowego całej odpowiedzi HTML, która wyjaśnia, dlaczego jest to najszybsza alternatywa w filmie, jest to, że kod HTML wyrenderowany podczas wstępnego żądania nawigacji może w pełni wykorzystać dostępny w przeglądarce parser HTML strumieniowania. Fragmenty kodu HTML wstawiane do dokumentu po wczytaniu strony (co jest często stosowane w modelu powłoki App Shell) nie mogą korzystać z tej optymalizacji.

Który model wybierzesz na etapie planowania implementacji skryptu service worker? Odpowiedź powłoki nie jest zaskakująca, ponieważ zależy to od tego, czy Twoja implementacja opiera się na systemie CMS i szablonach częściowych (zaleta: strumień), od tego, czy spodziewasz się pojedynczych, dużych ładunków HTML, które przyniosą korzyści z renderowania progresywnego (zalet: strumień), a także od tego, czy Twoja aplikacja internetowa jest najlepiej modelowana jako aplikacja na jednej stronie (funkcja w wersji jednostronicowej (zaawansowana: aplikacja) oraz czy jest obsługiwana w danej wersji.

Wciąż w początkowej fazie tworzenia odpowiedzi dotyczących strumieniowania opartych na mechanizmach Service Worker przez mechanizmy Service Worker czekamy, aż różne modele dojdą do dojrzałości, a zwłaszcza udostępnimy kolejne narzędzia do automatyzacji typowych przypadków użycia.

Szczegółowa analiza strumieni

Jeśli tworzysz własne, czytelne strumienie, samo wywoływanie controller.enqueue() bez dyskryminacji może nie być wystarczające lub efektywne. Jake opowiada szczegółowo o tym, jak jednocześnie można wykorzystać metody start(), pull() i cancel() w celu utworzenia strumienia danych dostosowanego do konkretnego zastosowania.

Jeśli potrzebujesz bardziej szczegółowych informacji, specyfikacja strumieni jest dla Ciebie.

Zgodność

W Chrome 52 dodaliśmy możliwość tworzenia obiektu Response w skrypcie service worker z użyciem komponentu ReadableStream.

Implementacja skryptu service worker w Firefoksie nie obsługuje jeszcze odpowiedzi opartych na kodach ReadableStream, ale w przypadku obsługi interfejsu Streams API występuje błąd związany ze śledzeniem.

Postęp w zakresie obsługi interfejsu Streams API w Edge i ogólnej pomocy mechanizmów Service Worker można śledzić na stronie stanu platformy firmy Microsoft.