Stream uw weg naar onmiddellijke reacties

Iedereen die servicemedewerkers heeft gebruikt, kan je vertellen dat ze helemaal asynchroon zijn. Ze vertrouwen uitsluitend op op gebeurtenissen gebaseerde interfaces, zoals FetchEvent , en gebruiken beloften om aan te geven wanneer asynchrone bewerkingen zijn voltooid.

Asynchroniciteit is net zo belangrijk, zij het minder zichtbaar voor de ontwikkelaar, als het gaat om antwoorden die worden gegeven door de ophaalgebeurtenishandler van een servicemedewerker. Streamingreacties zijn hier de gouden standaard: ze zorgen ervoor dat de pagina die het oorspronkelijke verzoek heeft gedaan, met het antwoord kan gaan werken zodra het eerste deel van de gegevens beschikbaar is, en maken mogelijk gebruik van parsers die zijn geoptimaliseerd voor streaming om de inhoud progressief weer te geven.

Wanneer u uw eigen gebeurtenishandler fetch schrijft, is het gebruikelijk om de methode respondWith() gewoon een Response (of een belofte voor een Response ) door te geven die u krijgt via fetch() of caches.match() , en er een einde aan te maken. Het goede nieuws is dat de Response die met beide methoden zijn gemaakt, al kunnen worden gestreamd! Het slechte nieuws is dat "handmatig" geconstrueerde Response niet kunnen worden gestreamd, althans tot nu toe. Dat is waar de Streams API in beeld komt.

Stromen?

Een stream is een gegevensbron die stapsgewijs kan worden gemaakt en gemanipuleerd , en die een interface biedt voor het lezen of schrijven van asynchrone gegevensfragmenten, waarvan op een bepaald moment slechts een subset in het geheugen beschikbaar kan zijn. Voorlopig zijn we geïnteresseerd in ReadableStream s , dat kan worden gebruikt om een Response object te construeren dat wordt doorgegeven aan 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);
});

De pagina waarvan het verzoek de fetch gebeurtenis heeft geactiveerd, krijgt een streamingreactie terug zodra event.respondWith() wordt aangeroepen, en zal uit die stroom blijven lezen zolang de servicemedewerker doorgaat met het in enqueue() van aanvullende gegevens. De reactie die van de servicemedewerker naar de pagina stroomt, is echt asynchroon en we hebben volledige controle over het vullen van de stream!

Gebruik in de echte wereld

Je hebt waarschijnlijk gemerkt dat het vorige voorbeeld een tijdelijke aanduiding /* your data here */ opmerkingen had, en weinig informatie bevatte over de feitelijke implementatiedetails. Hoe zou een voorbeeld uit de praktijk eruit zien?

Jake Archibald (niet verrassend!) heeft een geweldig voorbeeld van het gebruik van streams om een ​​HTML-antwoord samen te voegen uit meerdere in de cache opgeslagen HTML-fragmenten, samen met 'live' gegevens gestreamd via fetch() - in dit geval inhoud voor zijn blog

Het voordeel van het gebruik van een streamingreactie is, zoals Jake uitlegt , dat de browser de HTML kan parseren en weergeven terwijl deze binnenkomt, inclusief het eerste bit dat snel uit de cache wordt geladen, zonder te hoeven wachten tot het ophalen van de volledige bloginhoud is voltooid. Hierdoor wordt optimaal gebruik gemaakt van de progressieve HTML-weergavemogelijkheden van de browser. Andere bronnen die ook geleidelijk kunnen worden weergegeven, zoals sommige beeld- en videoformaten, kunnen ook profiteren van deze aanpak.

Stromen? Of app-shells?

De bestaande best practices rond het gebruik van servicemedewerkers om uw webapps aan te drijven, zijn gericht op een App Shell + dynamisch inhoudsmodel . Die aanpak is gebaseerd op het agressief cachen van de 'shell' van uw webapplicatie (de minimale HTML, JavaScript en CSS die nodig zijn om uw structuur en lay-out weer te geven) en vervolgens het laden van de dynamische inhoud die nodig is voor elke specifieke pagina via een verzoek aan de clientzijde.

Streams brengen een alternatief met zich mee voor het App Shell-model, waarbij er een vollediger HTML-antwoord naar de browser wordt gestreamd wanneer een gebruiker naar een nieuwe pagina navigeert. Het gestreamde antwoord kan gebruik maken van in de cache opgeslagen bronnen (zodat het nog steeds snel het eerste stukje HTML kan leveren, zelfs als het offline is!), maar uiteindelijk lijken ze meer op traditionele, door de server weergegeven antwoordteksten. Als uw webapp bijvoorbeeld wordt aangedreven door een contentmanagementsysteem dat HTML op de server rendert door gedeeltelijke sjablonen aan elkaar te plakken, vertaalt dat model zich rechtstreeks in het gebruik van streaming-reacties, waarbij de sjabloonlogica wordt gerepliceerd in de servicemedewerker in plaats van op uw server. Zoals de volgende video laat zien, kan het snelheidsvoordeel dat gestreamde reacties bieden voor dat gebruik opvallend zijn:

Een belangrijk voordeel van het streamen van de volledige HTML-reactie, waarin wordt uitgelegd waarom dit het snelste alternatief in de video is, is dat HTML die wordt weergegeven tijdens het eerste navigatieverzoek volledig kan profiteren van de streaming HTML-parser van de browser. Stukken HTML die in een document worden ingevoegd nadat de pagina is geladen (zoals gebruikelijk is in het App Shell-model) kunnen geen voordeel halen uit deze optimalisatie.

Dus als u zich in de planningsfase bevindt van de implementatie van uw servicemedewerker, welk model moet u dan gebruiken: gestreamde antwoorden die geleidelijk worden weergegeven, of een lichtgewicht shell gekoppeld aan een verzoek aan de clientzijde voor dynamische inhoud? Het antwoord is, niet verrassend, dat het ervan afhangt: of je een bestaande implementatie hebt die afhankelijk is van een CMS en gedeeltelijke sjablonen (voordeel: stream); of u afzonderlijke, grote HTML-payloads verwacht die baat zouden hebben bij progressieve weergave (voordeel: stream); of uw webapp het beste kan worden gemodelleerd als een applicatie van één pagina (voordeel: App Shell); en of je een model nodig hebt dat momenteel wordt ondersteund in de stabiele releases van meerdere browsers (voordeel: App Shell).

We bevinden ons nog in de begindagen van door servicemedewerkers aangedreven streamingreacties, en we kijken ernaar uit om de verschillende modellen volwassen te zien worden en vooral om te zien dat er meer tools worden ontwikkeld om veelvoorkomende gebruiksscenario's te automatiseren.

Duik dieper in beken

Als u uw eigen leesbare streams samenstelt, is het simpelweg aanroepen van controller.enqueue() wellicht niet voldoende of efficiënt. Jake gaat dieper in op hoe de methoden start() , pull() en cancel() samen kunnen worden gebruikt om een ​​gegevensstroom te creëren die is afgestemd op jouw gebruiksscenario.

Voor degenen die nog meer details willen, heeft de Streams-specificatie de oplossing.

Compatibiliteit

Ondersteuning voor het construeren van een Response object binnen een servicemedewerker met behulp van een ReadableStream als bron is toegevoegd in Chrome 52 .

De service worker-implementatie van Firefox ondersteunt nog geen reacties die worden ondersteund door ReadableStream 's, maar er is een relevante trackingbug voor Streams API-ondersteuning die u kunt volgen.

De voortgang van niet-voorgefixeerde Streams API-ondersteuning in Edge, samen met de algehele ondersteuning van servicemedewerkers , kan worden gevolgd op de Platformstatuspagina van Microsoft.