
Kuidas voogedastada reaalajas raportit ilma virvenduseta
Kui koosolek lõpeb, täitub GeekBye kokkuvõte nüüd reaalajas, selle asemel et sundida sind spinnerit vahtima. Selleks et voogedastuse liides tunduks rahulik, mitte närviline, tuli virvendus lahendada kaks korda — üks kord struktureeritud väljade jaoks, teine kord markdown''i jaoks — ja teine lahendus oli renderdaja, mis meil juba olemas oli.
Enne seda väljalaset tähendas GeekBye koosoleku lõpetamine spinneri vahtimist. Rakendus saatis kogu su transkriptsiooni analüüsiks ära, ootas ära terve kokkuvõtte — skoori, põhipunktid, tegevused, kõik — ja kallas selle siis korraga ekraanile. Toimis, aga see tundus aeglane, sest sa vahtisid tühjust, samal ajal kui see töötas.
GeekBye v1.6.13–v1.6.15 muutis selle raportiks, mis voogedastub reaalajas, väli väli haaval, sedamööda kuidas seda genereeritakse. Huvitav osa pole voogedastus ise — see on kõik see, mida me pidime tegema, et voogedastus ei näeks välja närviline. Rahulik voogedastus ja tõmblev voogedastus on sama funktsioon väga erineva teostusega.
Virvendus, esimene osa: ära lase igal tükil Reacti raputada
Kokkuvõte pole üks pläru — see on struktureeritud väljad (üldskoor, põhipunktid, tegevused ja nii edasi), mille backend saadab ükshaaval server-sent events'i kaudu. Naiivne viis seda renderdada on uuendada Reacti olekut iga saabuva sündmuse peale.
Tee nii ja saad virvenduse. Iga saabuv väli käivitab oma ümberrenderduse; komponendid, mis näitasid "tühja", monteeruvad, siis ühenduvad lahti, siis monteeruvad uuesti "täidetuna"; paigutus tõmbleb iga paketi peale. Raport paneb end kokku su silme all kõige halvemal võimalikul viisil — nähtavalt, närviliselt.
Lahendus on kaheosaline distsipliin. Esiteks, kogu saabuvad väljad ref'i — muudetavasse hoidjasse, mis ei käivita renderdusi — ja avalda Reacti olekusse üks kord tüki kohta värske koopiaga, nii et puu uueneb sihilikult, mitte iga mikrosündmuse peale. Teiseks, renderda kogu aeg üks stabiilne komponendipuu, olgu voogedastades või valmis, koos reasiseste kohatäidetega väljadele, mis pole veel saabunud:
// accumulate in a ref, publish once per chunk
partialRef.current = { ...partialRef.current, [field]: value }
setPartial({ ...partialRef.current })
// one tree, never swapped; missing fields show a placeholder in place
const report = isStreaming ? partial : saved
<Score>{report.overallScore ?? '…'}</Score>
Komponent, mis näitab skoori, ei ühendu kunagi lahti — see lihtsalt näitab …, kuni number kohale jõuab, ja siis numbrit. Miski ei tõmble. Ja täielikult kokku pandud objekt salvestatakse lõpus ikkagi kohalikku andmebaasi, nii et voogedastuse liides ja püsiv salvestus eksisteerivad koos ilma omavahel võitlemata.
Virvendus, teine osa: renderdaja, mis meil juba olemas oli
Teine virvendus on peenem ja elab markdown''i endas. Raport renderdab rikkalikku markdown''i — pealkirju, rasvast kirja, loendeid, koodiplokke. Aga markdown, mis saabub voo keskel, on igal antud hetkel poolik. Loend seni ühe punktiga. Koodipiire, mis on avatud, aga mitte suletud. Rasva marker, millel pole veel paarilist.
Tavaline markdown''i renderdaja parsib terve sõne iga tokeni peale uuesti ning see poolik olek parsitakse iga kord millekski erinevaks — nii loend virvendab, koodiplokk vilgub avatud ja suletud, paigutus hüppab, kui tokenid valmivad. See on sama närviline kokkupanek nagu enne, üks kiht allpool.
Lahendus oli peaaegu piinlikult käepärast: me juba kasutasime voogedastusteadlikku markdown''i renderdajat reaalajas AI-vestluse jaoks. Renderdaja, mis on ehitatud taluma mittetäielikke tokeneid — renderdama poolikut markdown''i stabiilselt ja seda alles siis paika panema, kui tokenid valmivad — selle asemel et iga kord nullist uuesti parsida. Raport pidi lihtsalt sama renderdajat kasutama. Me olime täpselt selle probleemi vestluse jaoks kuid varem lahendanud; raporti "lahendus" oli mõista, et meil oli tööriist juba olemas, ja suunata see teisele pinnale. Taaskasutus võitis ümberehituse.
Boonus: vormindatud kopeerimine on lõikelaua vormingu probleem
Kui raport hakkas hea välja nägema, tahtsid inimesed seda kleepida — dokumenti, e-kirja — ja säilitada vorminduse ning ekraanipildid. Instinkt on serialiseerida raport markdown''i sõneks ja loota, et sihtkoht renderdab selle. Tavaliselt ei renderda.
Tõeline vastus on see, et lõikelaud hoiab mitut esitust korraga. Nii kirjutame me kaks: HTML-versiooni vorminduse säilitades ja ekraanipiltidega reasiseste piltidena manustatuna ning lihttekstilise varuvariandi sihtkohtadele, mis HTML-i ei võta. Kleebi rikkalikku redaktorisse ja saad vormindatud raporti piltidega; kleebi lihtteksti kasti ja saad puhta teksti. "Vormindatud kopeerimine" polnud kunagi serialiseerimise probleem — see oli õige-lõikelaua-vormingu-valimise probleem.
Sama väljalase andis raportile ka vestlusstiilis Me / Them sildid, kaks kõnelejat vastaskülgedel joondatud, nii et transkriptsioon loeb nagu vestlus, mis see oligi, ning liigutas su enda ekraanipildid nende omale poolele, et sobitada. (Üks selle akna väljalase, v1.6.14, oli puhas ümberehitus — sellel pole lugu, ja aus on seda öelda.)
Kolm asja, mida voogedastuse liides meile õpetas
- Puhverda voogedastatud väljad, avalda üks kord tüki kohta. Kui lased igal server-sent event''il oma olekuuuendust juhtida, siis see ongi virvendus. Ref-akumulaator pluss üks avaldus tüki kohta, puusse, mis kunagi ei ühendu lahti, muudab närvilise kokkupaneku rahulikuks täitumiseks.
- Kõik, mis voogedastub, vajab voogedastusteadlikku renderdajat. Renderdaja, mis parsib terve sõne iga tokeni peale uuesti, virvendab pooliku markdown''i peal. Kasuta sellist, mis on ehitatud mittetäieliku sisendi jaoks — ja kui sul on juba üks teise pinna jaoks olemas, taaskasuta seda enne, kui ehitad teise.
- Vormindatud kopeerimine on lõikelaua vormingutest, mitte serialiseerimisest. Kirjuta
text/htmlmanustatud piltidega jatext/plainvaruvariant. Lõikelaud loodi mõlemat kandma; kasuta seda.
See on viies peatükk usaldusväärsuse-ja-viimistluse loos, millest saab GeekBye v2. Eelmise peatüki leiad earbudside vea juurest (v1.6.12); selle kohta, kuidas sama server-sent-events''i idee hiljem sai terveks varutranspordiks, reaalajas transkriptsioon, kui tulemüür blokeerib WebSocketid (v2.0.8); ja kogu kaare kohta, tarkvara täiuseni skeepimise anatoomia.