- Wie bei progressivem JPEG werden auch JSON-Daten zunächst in unvollständigem Zustand übertragen, sodass der Client nach und nach den gesamten Inhalt nutzen kann
- Das bestehende JSON-Parsing-Verfahren hat das Effizienzproblem, dass vor dem vollständigen Empfang der gesamten Daten keinerlei Verarbeitung möglich ist
- Mit einem Breadth-first-Ansatz werden Daten in mehrere Chunks (Teile) aufgeteilt; noch nicht vorbereitete Teile werden als Promise dargestellt und nach und nach aufgefüllt, sobald sie bereit sind, sodass der Client auch unvollständige Daten nutzen kann
- Dieses Konzept ist die zentrale Innovation von React Server Components (RSC), und mit
<Suspense> werden beabsichtigte stufenweise Ladezustände gesteuert
- Durch die Trennung von Daten-Streaming und bewusst gestaltetem UI-Ladefluss wird eine flexiblere User Experience möglich
Die Idee von progressivem JPEG und progressivem JSON
- Ein progressives JPEG lädt ein Bild nicht auf einmal von oben nach unten, sondern zeigt zunächst die gesamte Ansicht unscharf und wird dann nach und nach schärfer
- Ähnlich kann ein progressiver Ansatz auch auf die JSON-Übertragung angewendet werden, sodass Teildaten sofort nutzbar sind, ohne auf die vollständige Fertigstellung zu warten
- Bei der beispielhaften JSON-Datenstruktur ist Parsing im üblichen Verfahren erst möglich, wenn wirklich das letzte Byte empfangen wurde
- Dadurch muss der Client auch auf langsame Teile des Servers (z. B. das Laden von comments aus einer langsamen DB) warten, bis alles übertragen wurde, was den aktuellen Standard sehr ineffizient macht
Grenzen von Streaming-JSON-Parsern
- Mit einem Streaming-JSON-Parser lässt sich ein unvollständiger (zwischenzeitlicher) Objektbaum erzeugen
- Wenn jedoch Felder einzelner Objekte (z. B. footer oder mehrere comment-Listen) nur teilweise übertragen werden, entstehen Probleme wie Typinkonsistenzen und schwer erkennbare Vollständigkeit, was die Nutzbarkeit verringert
- Ähnlich wie beim Streaming-Rendering von HTML tritt auch hier bei sequentieller Stream-Verarbeitung dasselbe Problem auf: Ein langsamer Teil verzögert das gesamte Ergebnis
- Das ist ein Grund, warum Streaming-JSON im Allgemeinen selten verwendet wird
Vorschlag für eine Progressive-JSON-Struktur
- Statt des bisherigen Depth-first-Streamings (also der Übertragung durch Traversierung bis in die tieferen Ebenen der Baumstruktur) wird ein Breadth-first-Ansatz eingeführt
- Zunächst wird nur das Top-Level-Objekt übertragen; untergeordnete Werte bleiben als Platzhalter ähnlich Promises bestehen und werden jeweils als Chunks aufgefüllt, sobald sie bereit sind
- Wenn der Server beispielsweise den asynchronen Ladevorgang eines Datenteils abgeschlossen hat, sendet er den entsprechenden Chunk, und der Client kann nur das nutzen, was bereits bereitsteht
- Dadurch wird asynchroner Datenempfang (frühes Laden) möglich, ohne auf das Ende aller langsamen Teile warten zu müssen
- Wenn der Client robust für nicht-sequenziellen und teilweise sequenziellen Empfang pro Chunk aufgebaut ist, kann der Server unterschiedliche Strategien zur Chunk-Aufteilung flexibel anwenden
Inlining und Outlining: effiziente Datenübertragung
- Ein progressives JSON-Streaming-Format kann wiederverwendete Objekte (z. B. dieselbe userInfo, auf die an mehreren Stellen verwiesen wird) auch ohne doppelte Speicherung in einen separaten Chunk auslagern und an allen Positionen mit derselben Referenz nutzen
- Nur langsame Teile werden als Platzhalter ausgelagert und übertragen, während der Rest sofort aufgefüllt wird, was einen effizienten Datenstrom ermöglicht
- Wenn dasselbe Objekt mehrfach vorkommt, kann es nur einmal übertragen und wiederverwendet (Outlining) werden
- Auf diese Weise lassen sich auch zyklische Referenzen (eine Struktur, in der ein Objekt auf sich selbst verweist) anders als in normalem JSON problemlos als indirekte Referenzstruktur zwischen Chunks natürlich serialisieren
Progressive-Streaming-Implementierung in React Server Components (RSC)
- Reale React Server Components sind ein repräsentatives Beispiel für die Anwendung des progressiven JSON-Streaming-Modells
- Der Server verwendet eine Struktur, in der externe Daten (z. B. Post, Comments) asynchron geladen werden
- Auf Client-Seite werden noch nicht eingetroffene Teile als Promise behandelt und die UI wird entsprechend der Reihenfolge der Bereitstellung schrittweise gerendert
- Mit Reacts
<Suspense> werden bewusst gestaltete Ladezustände definiert
- Um unnötige Sprünge in der UI aus Sicht der User Experience zu vermeiden, werden Promise-Zustände (Lücken) nicht sofort gezeigt; stattdessen lässt sich mit dem
<Suspense>-Fallback ein stufenweiser Ladevorgang inszenieren
- Selbst wenn Daten schnell eintreffen, kann der Entwickler steuern, dass die tatsächliche UI entsprechend der geplanten Stufen progressiv sichtbar wird
Zusammenfassung und Implikationen
- Die zentrale Innovation von React Server Components besteht darin, Props im Komponentenbaum schrittweise von außen nach innen zu streamen
- Daher muss nicht gewartet werden, bis der Server sämtliche Daten vollständig vorbereitet hat; stattdessen können wichtige Teile zuerst gezeigt und Ladezustände fein gesteuert werden
- Nicht nur das Streaming selbst, sondern auch strukturelle Unterstützung wie das Programmiermodell, das es nutzt (z. B. Reacts
<Suspense>), ist notwendig
- So lassen sich Engpässe bisheriger Übertragungsverfahren abmildern, etwa das Problem, dass ein einzelner langsamer Datenteil alles verzögert
1 Kommentare
Hacker-News-Kommentare
nullnicht aus; man braucht zusätzliche Informationen darüber, welche Daten gerade noch ausstehenJSON.parse