de Marvin Frachet

React-cache, tranșarea timpului și preluarea cu un API sincron

React cache transarea timpului si preluarea cu un API sincron

Ei bine, anul acesta pare a fi anul React. Probabil ați auzit de noua caracteristică criminală care vine cu 16.7-alpha.0 – Hooks. Probabil ați auzit și despre alte lucruri grozave și interesante, cum ar fi Time Slicing sau chiar Suspans.

Acest articol nu vizează descrierea cum să utilizați unele dintre noile caracteristici, ci mai degrabă pentru a demonstra cum ar fi putut fi create. Doar pentru a înțelege cu ce ne jucăm.

De asemenea, este scris în felul în care am descoperit funcția. Probabil nu este așa cum a fost gândit, dar așa am obținut punctele.

Ce veți găsi în timp ce citiți:

  • JavaScript asincronizat și bucla evenimentului
  • Efecte algebrice în React, cu un exemplu
  • Fazele de fibră și reacție

De ce am scris această postare?

Ceea ce m-a făcut să vreau să scriu această postare a fost această caracteristică specială și experimentală, care permite utilizarea asincron operații folosind un sincron API:

const bulbasaur = ApiResource.read()?… Ce? Sincron?!

react-cache biblioteca creează capacitatea de a utiliza operațiuni asincrone cu un API sincron. Aceasta este caracteristica care m-a făcut să vreau să aflu cum funcționează React sub capotă. Iată o prezentare prezentată de Dan Abramov și Andrew Clark pe această bibliotecă:

Cum este posibil chiar asta? Cum putem obține unele date la distanță folosind apeluri sincrone?

Să ne adâncim în acest exemplu și să încercăm să înțelegem cum react-cache implementează o astfel de funcționalitate și descoperă cum poate funcționa. Această poveste începe cu arhitectura din fibre.

Controlul operațiunilor JavaScript

Arhitectura cu fibre permite React să preia controlul asupra execuțiilor sarcinilor. A fost construit pentru a rezolva multiple probleme de care React a suferit. Iată cele două care mi-au atras atenția:

  • prioritizarea față de evenimente specifice, cum ar fi introducerea utilizatorului față de preluarea datelor
  • împărțirea asincronă React calcul pentru a păstra disponibilitatea firului principal și pentru a evita blocarea acestuia în timpul proceselor de redare lungi

Tot ceea ce declanșează o schimbare de stare – nu numai cu React – în cadrul unei aplicații JavaScript se datorează operațiilor asincrone. Acestea includ setTimeout , fetch și ascultători de evenimente.

Operațiunile asincrone sunt gestionate prin mai multe concepte de bază JavaScript:

  • sarcini (micro, macro, randare etc …)
  • bucla eveniment
  • callstack

Dacă nu sunteți familiarizați cu aceste concepte, vă sugerez să aruncați o privire la acest videoclip de Jake Archibald:

Datorită fibrelor, intrările utilizatorului sunt rezolvat înainte de alte operații asincrone, cum ar fi preluarea apelurilor.

Cum este posibil chiar asta?

Ei bine, discuția lui Archibald de mai sus a fost prima piatră pavată a propriei mele căi de învățare despre cum funcționează bucla evenimentului. El spune că micro-sarcinile – generate de API-ul Promise, de exemplu – sunt executate și spălate inainte de următoarea sarcină macro. Acest proces utilizează metode bazate pe apelare, cum ar fi setTimeout.

Așadar, dacă vă amintiți comparația mea „date introduse de utilizator față de preluarea datelor”, cum a făcut echipa fetch rezoluții după onChange rezolutii?

Niciunul dintre aceste concepte nu se încadrează în aceeași specificație, WhatWG / HTML5 / Ecma-262și sunt furnizate din diferite locuri, cum ar fi browserul sau motoarele JS.

Adică, cum ar trebui să rezolvăm un Promise dupa o setTimeout?

Mi s-a părut absolut nebunesc și mi-a fost foarte greu să-mi fac o idee despre cum ar putea funcționa. Faptul este că are loc la un nivel superior.

Mai târziu, am urmărit discuția incredibilă de la Brandon Dail la React Rally. Aceasta prezintă noul Caracteristici Time Slicing și Suspense care au fost livrate datorită arhitecturii din fibra React:

Potrivit Dail, fibra este la fel ca obișnuitul callstack JavaScript în care fiecare element din stivă se numește a fibră. Este diferit de callstack-ul pe care se bazează rame care reprezintă funcții (+ metadate). Mai degrabă, o fibră reprezintă A componentă (+ metadate). Să vedem o fibră ca o cutie imensă în jurul unei componente care știe totul despre ea.

Există o diferență importantă între aceste două concepte.

Pe de o parte, callstack-ul este o funcționalitate care a fost construită deasupra partea nativă conducând Cod JavaScript. Acesta își propune să stiveze fiecare apel de funcție JavaScript și să le ruleze pe cont propriu. De fiecare dată când apelăm o funcție este adăugată la stivă. Fără callstack, nu am fi capabili să avem urme de stivă clare și detaliate. Și întrucât callstack-ul nu este accesibil dintr-un cod JavaScript, este cu adevărat dificil și chiar imposibil să preia controlul asupra acestuia.

Pe de altă parte, fibrele – ca un teanc de fibre – reprezintă același concept, dar încorporat Cod JavaScript. Cea mai mică unitate nu este funcții, ci o componentă. De fapt, rulează într-un univers JavaScript.

Faptul că arhitectura fibrelor este complet integrată în JavaScript înseamnă că o putem folosi, accesa și modifica. Putem lucra la el folosind JavaScript standard.

Ceea ce m-a condus în direcția greșită a fost că am crezut că React utilizează o soluție pentru a întrerupe modul în care funcționează JavaScript. Nu este cazul. Fibrele sunt pur și simplu obiecte JavaScript care dețin informații despre componentele React și care pot interacționa cu ciclurile lor de viață. Poate acționa numai în funcție de funcționalitățile interne React.

Ideea este nu pentru a redefini modul în care ar trebui să funcționeze JavaScript, cum ar fi să spui asta fetch rezoluția microtask-ului ar trebui executată înainte de sarcinile de apel invers. Este mai mult asupra metodelor React ar trebui să fie chemat sau nu într-un context specific, cum ar fi întreruperea diferitelor apeluri ale metodei ciclului de viață.

Hei, asteapta! Spui că fibrele pot controla absolut totul într-o aplicație React? Dar cum poate o componentă să-i spună lui React să nu mai facă nimic?

Efecte algebrice, da, dar în JavaScript vă rog

React este capabil să controleze componentele și să știe dacă o componentă rulează, datorită arhitecturii de fibră. Ceea ce lipsește acum este o modalitate de a spune React că s-a schimbat ceva pentru o anumită componentă, deci se va ocupa de această modificare.

Aici e locul efecte algebrice intră în joc.

Efectele algebrice nu sunt ceva care există în JavaScript. Voi încerca să explic ceea ce sunt cu o explicație de nivel superior.

Efectele algebrice sunt un concept care permite trimiterea unor informații undeva, cam ca un dispecer. Ideea este de a apela o funcție specifică care va întrerupe funcția care rulează în prezent într-o poziție precisă pentru a permite unei funcții părinte să se ocupe de calcul. Când se termină calculul părinte, acesta poate relua programul în poziția inițială unde au fost trimise informațiile.

Unele limbi precum OCaml sau Efic beneficiați de aceste caracteristici în mod nativ. Aceasta este o abstractizare cu adevărat interesantă, deoarece detaliile implementării se bazează doar pe părinte:

Nu ar fi minunat să ai o astfel de caracteristică în JavaScript?

Echipa React a creat o abordare similară într-un context React care se ocupă de JavaScript try/catch bloc. Potrivit Dail, este cel mai apropiat concept disponibil în JavaScript.

Aruncarea ceva permite trimiterea informațiilor către un părinte, undeva. Primul părinte care prinde informația este capabil să se ocupe de ea și să facă calcule pe ea.

Un exemplu este mai bun decât o mie de cuvinte

Imaginați-vă următorul cod care încearcă să aducă Bulbasaur folosind un API sincron:

Această bucată de cod poate fi ciudată, deoarece nu este foarte obișnuit să preluați date folosind un API sincron. Să sărim în interiorul customFetch implementarea funcției:

Oh, așteptați! Acest lucru nu pare absolut o preluare! Nu înțeleg deloc ce își propune să facă această funcție …

Ei bine, imaginează-ți ceva în jurul componentei, să spunem o fibră care arată ca:

Luați ceva timp pentru a citi codul.

Acum, să sărim la customFetch implementare:

Partea importantă din fragmentele anterioare este try/catch bloc.

Să rezumăm ce se întâmplă prin aceste diferite bucăți de cod:

  • Pokemon componentă apelează customFetch metodă.
  • customFetch metoda încearcă să citească memoria cache internă, dar este goală. Deci aruncă ceva / undeva – efecte algebrice.
  • fiber părintele prinde acele informații, le manipulează și preia datele. Apoi populează customFetch cache cu datele.
  • O redare apare în Component(args)și, acum, customFetch memoria cache este plină. Datele sunt acum disponibile în componentă utilizând un API sincron.

Uită-te la react-cache detalii de implementare și verificați diferitele aruncări.

Este posibil ca ceva să vă fi atras atenția în timpul acestui proces: render a fost chemat de două ori. Unul pentru aruncarea erorii – pauză componenta – și una pentru obținerea datelor – reluând componenta. Este în regulă cu React să declanșezi mai multe render apeluri, deoarece este doar o funcție pură – nu are efecte secundare pe cont propriu.

Stai ce? render nu are efecte secundare? Dar DOM?

Reacționează fazele

Dacă lucrați cu React de mult timp, este posibil să fi auzit că nu este o practică bună să redați redarea de mai multe ori. Înainte de arhitectura fibrelor, de fiecare dată când apelam funcția de redare React făcea câteva calcule interne și apoi modifica DOM în consecință. De exemplu, acest lucru s-a întâmplat când ați apelat funcția de redare setState. Procesul a fost subliniat:

setStaterender → comparați nodurile virtuale → actualizați nodurile DOM

În ceea ce privește fibrele, procesul este puțin diferit. A introdus un concept de coadă și loturi care permite modificări DOM de înaltă performanță.

Ideea este destul de simplă. Presupunem că ecranele pot rula ~ 60 de cadre pe secundă. Din această ipoteză și folosind funcțiile JavaScript disponibile, este posibil să faceți unele calcule și modificări DOM doar la fiecare ~ 16,7 ms. Cu fibră, React poate face modificări multiple și le poate comite de aproximativ 60 de ori pe secundă.

Acest tip de modificare a permis React să se împartă în trei faze cu propriile avantaje și particularități:

React cache transarea timpului si preluarea cu un API sincron
Dan Abramov despre fazele React
  • Faza de redare este pură și deterministă. Nu are efecte secundare și diferitele funcții din care este compus pot fi numite de mai multe ori. faza de redare este întreruptibilă – nu este render funcție care se află în modul pauză, dar întreaga fază
  • Fraza de precomitere urmărește să ofere acces la starea reală DOM, cum ar fi pozițiile barei de derulare, în modul de citire.
  • Faza de validare modifică de fapt DOM și nu este intreruptibil. React nu poate fi întrerupt în timpul acelei faze.

Acest set de trei faze a introdus funcțiile Time Slicing. React este capabil să facă o pauză în timpul fazei de redare, între două apeluri de funcții componente și să reia acea fază atunci când este necesar.

În fibre, render își propune doar să obțineți cea mai recentă reprezentare disponibilă a unei componente bazată pe starea sa internă pentru a face unele comparații și pentru a ști dacă React trebuie să schimbe sau nu DOM-ul. Dacă este necesară o modificare de validare, aceasta va adăuga modificarea la o coadă „work in progress”.

Echipa React a realizat îmbunătățiri uriașe ale performanței datorită React Concurrent (Time Slicing + Suspense) și arhitecturii fibrelor. Au creat soluții alternative pentru a contracara diferitele probleme ale browserului, cum ar fi prioritizarea evenimentelor și concurența.

Dacă facem un pas înapoi, nu asta au arătat ei? Prioritizarea pare a fi noua provocare pentru browser-ul și cadrele front-end.

Alte echipe lucrează, de asemenea, la îmbunătățirea stadiului actual al tehnicii și chiar propun API viitoare. Aceasta este ideea Google: