Gestionarea sarcinilor de lungă durată într-o aplicație React cu lucrători web

Publicat: 2022-03-10
Rezumat rapid ↬ În acest tutorial, vom învăța cum să folosim API-ul Web Worker pentru a gestiona sarcinile consumatoare de timp și de blocare a interfeței de utilizator într-o aplicație JavaScript prin construirea unui exemplu de aplicație web care folosește lucrătorii web. În cele din urmă, vom încheia articolul transferând totul într-o aplicație React.

Timpul de răspuns este o mare problemă atunci când vine vorba de aplicații web. Utilizatorii solicită răspunsuri instantanee, indiferent de ceea ce face aplicația dvs. Indiferent dacă afișează doar numele unei persoane sau strânge numere, utilizatorii aplicației web cer ca aplicația dvs. să răspundă la comanda lor de fiecare dată. Uneori, acest lucru poate fi greu de realizat, având în vedere natura unică a JavaScript-ului. Dar, în acest articol, vom afla cum putem folosi API-ul Web Worker pentru a oferi o experiență mai bună.

Scriind acest articol, am făcut următoarele presupuneri:

  1. Pentru a putea urmări, ar trebui să fiți cel puțin familiarizat cu JavaScript și cu API-ul document;
  2. De asemenea, ar trebui să aveți cunoștințe practice despre React, astfel încât să puteți începe cu succes un nou proiect React utilizând aplicația Create React.

Dacă aveți nevoie de mai multe informații despre acest subiect, am inclus o serie de link-uri în secțiunea „Resurse suplimentare” pentru a vă ajuta să fiți la curent.

Mai întâi, să începem cu Web Workers.

Ce este un lucrător web?

Pentru a înțelege lucrătorii web și problema pe care trebuie să o rezolve, este necesar să înțelegeți cum este executat codul JavaScript în timpul execuției. În timpul rulării, codul JavaScript este executat secvenţial și pas cu pas. Odată ce o bucată de cod se termină, apoi următoarea din linie începe să ruleze și așa mai departe. În termeni tehnici, spunem că JavaScript este cu un singur thread. Acest comportament implică faptul că, odată ce o bucată de cod începe să ruleze, fiecare cod care vine după trebuie să aștepte ca acel cod să termine execuția. Astfel, fiecare linie de cod „blochează” execuția a tot ce urmează după ea. Prin urmare, este de dorit ca fiecare bucată de cod să se termine cât mai repede posibil. Dacă o bucată de cod durează prea mult să se termine, programul nostru ar părea că nu mai funcționează. Pe browser, aceasta se manifestă ca o pagină înghețată, care nu răspunde. În unele cazuri extreme, fila se va îngheța complet.

Imaginați-vă că conduceți pe o singură bandă. Dacă vreunul dintre șoferii din fața dvs. se oprește din mișcare din orice motiv, atunci aveți un blocaj în trafic. Cu un program precum Java, traficul ar putea continua pe alte benzi. Astfel, se spune că Java este multi-threaded. Lucrătorii web sunt o încercare de a aduce un comportament cu mai multe fire în JavaScript.

Captura de ecran de mai jos arată că API-ul Web Worker este acceptat de multe browsere, așa că ar trebui să vă simțiți încrezători în utilizarea acestuia.

Se afișează diagrama de asistență pentru browser pentru lucrătorii web
Suport pentru browser Web Workers. (Previzualizare mare)

Lucrătorii web rulează în fire de execuție de fundal fără a interfera cu interfața de utilizare și comunică cu codul care i-a creat prin intermediul handlerelor de evenimente.

O definiție excelentă a unui lucrător web vine de la MDN:

„Un lucrător este un obiect creat folosind un constructor (de exemplu, Worker() care rulează un fișier JavaScript numit - acest fișier conține codul care va rula în firul de lucru; lucrătorii rulează într-un alt context global care este diferit de window curentă. Astfel , folosind comanda rapidă pentru window pentru a obține domeniul global actual (în loc de self într-un Worker va returna o eroare."

Un lucrător este creat folosind constructorul Worker .

 const worker = new Worker('worker-file.js')

Este posibil să rulați majoritatea codului în interiorul unui lucrător web, cu unele excepții. De exemplu, nu puteți manipula DOM-ul din interiorul unui lucrător. Nu există acces la document API.

Lucrătorii și firul care îi generează își trimit mesaje unul altuia folosind metoda postMessage() . În mod similar, ei răspund la mesaje folosind handlerul de evenimente onmessage . Este important să obțineți această diferență. Trimiterea mesajelor se realizează folosind o metodă; primirea unui mesaj înapoi necesită un handler de evenimente. Mesajul primit este conținut în atributul de data al evenimentului. Vom vedea un exemplu în acest sens în secțiunea următoare. Dar permiteți-mi să menționez rapid că genul de muncitor despre care am discutat se numește „lucrător dedicat”. Aceasta înseamnă că lucrătorul este accesibil doar scriptului care l-a numit. De asemenea, este posibil să aveți un lucrător care este accesibil din mai multe scripturi. Aceștia se numesc lucrători partajați și sunt creati folosind constructorul SharedWorker , așa cum se arată mai jos.

 const sWorker = new SharedWorker('shared-worker-file.js')

Pentru a afla mai multe despre Lucrători, consultați acest articol MDN. Scopul acestui articol este de a vă ajuta să utilizați lucrătorii web. Să ajungem la el calculând al n-lea număr Fibonacci.

Mai multe după săritură! Continuați să citiți mai jos ↓

Calcularea celui de-al N-lea număr Fibonacci

Notă: Pentru aceasta și următoarele două secțiuni, folosesc Live Server pe VSCode pentru a rula aplicația. Cu siguranță poți folosi altceva.

Aceasta este secțiunea pe care o așteptați. Vom scrie în sfârșit niște cod pentru a vedea lucrătorii web în acțiune. Ei bine, nu atât de repede. Nu am aprecia munca pe care o face un Web Worker decât dacă ne confruntăm cu genul de probleme pe care le rezolvă. În această secțiune, vom vedea un exemplu de problemă, iar în secțiunea următoare, vom vedea cum un lucrător web ne ajută să facem mai bine.

Imaginează-ți că construiești o aplicație web care le-a permis utilizatorilor să calculeze al n-lea număr Fibonacci. În cazul în care sunteți nou în ceea ce privește termenul „număr Fibonacci”, puteți citi mai multe despre acesta aici, dar în rezumat, numerele Fibonacci sunt o secvență de numere astfel încât fiecare număr este suma celor două numere precedente.

Din punct de vedere matematic, se exprimă astfel:

Astfel, primele câteva numere ale șirului sunt:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89 ...

În unele surse, secvența începe de la F 0 = 0 , caz în care formula de mai jos este valabilă pentru n > 1 :

În acest articol vom începe cu F 1 = 1. Un lucru pe care îl putem vedea imediat din formulă este că numerele urmează un model recursiv. Sarcina acum este de a scrie o funcție recursivă pentru a calcula al n-lea număr Fibonacci (FN).

După câteva încercări, cred că puteți găsi cu ușurință funcția de mai jos.

 const fib = n => { if (n < 2) { return n // or 1 } else { return fib(n - 1) + fib(n - 2) } }

Funcția este simplă. Dacă n este mai mic decât 2, returnează n (sau 1), în caz contrar, returnează suma FN-urilor n-1 și n-2 . Cu funcții de săgeți și operator ternar, putem veni cu o singură linie.

 const fib = n => (n < 2 ? n : fib(n-1) + fib(n-2))

Această funcție are o complexitate de timp de 0(2 n ) . Aceasta înseamnă pur și simplu că, pe măsură ce valoarea lui n crește, timpul necesar pentru a calcula suma crește exponențial. Acest lucru face o sarcină cu adevărat de lungă durată, care ar putea interfera cu interfața noastră de utilizare, pentru valori mari de n. Să vedem asta în acțiune.

Notă : aceasta nu este în niciun caz cea mai bună modalitate de a rezolva această problemă specială. Alegerea mea de a folosi această metodă este în scopul acestui articol.

Pentru a începe, creați un folder nou și denumiți-l după cum doriți. Acum, în interiorul acelui folder, creați un folder src/ . De asemenea, creați un fișier index.html în folderul rădăcină. În interiorul folderului src/ , creați un fișier numit index.js .

Deschideți index.html și adăugați următorul cod HTML.

 <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="styles.css"> </head> <body> <div class="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div class="body-container"> <p id='error' class="error"></p> <div class="input-div"> <input id='number-input' class="number-input" type='number' placeholder="Enter a number" /> <button id='submit-btn' class="btn-submit">Calculate</button> </div> <div id='results-container' class="results"></div> </div> <script src="/src/index.js"></script> </body> </html>

Această parte este foarte simplă. În primul rând, avem un titlu. Apoi avem un container cu o intrare și un buton. Un utilizator ar introduce un număr, apoi face clic pe „Calculați”. Avem și un container pentru a păstra rezultatul calculului. În cele din urmă, includem fișierul src/index.js într-o etichetă de script .

Puteți șterge linkul pentru foaia de stil. Dar dacă aveți puțin timp, am definit niște CSS pe care le puteți utiliza. Doar creați fișierul styles.css în folderul rădăcină și adăugați stilurile de mai jos:

 body { margin: 0; padding: 0; box-sizing: border-box; } .body-container, .heading-container { padding: 0 20px; } .heading-container { padding: 20px; color: white; background: #7a84dd; } .heading-container > h1 { margin: 0; } .body-container { width: 50% } .input-div { margin-top: 15px; margin-bottom: 15px; display: flex; align-items: center; } .results { width: 50vw; } .results>p { font-size: 24px; } .result-div { padding: 5px 10px; border-radius: 5px; margin: 10px 0; background-color: #e09bb7; } .result-div p { margin: 5px; } span.bold { font-weight: bold; } input { font-size: 25px; } p.error { color: red; } .number-input { padding: 7.5px 10px; } .btn-submit { padding: 10px; border-radius: 5px; border: none; background: #07f; font-size: 24px; color: white; cursor: pointer; margin: 0 10px; }

Acum deschideți src/index.js , să-l dezvoltăm încet. Adaugă codul de mai jos.

 const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } }; const textCont = (n, fibNum, time) => { const nth = ordinal_suffix(n); return ` <p id='timer'>Time: <span class='bold'>${time} ms</span></p> <p><span class="bold" id='nth'>${nth}</span> fibonnaci number: <span class="bold" id='sum'>${fibNum}</span></p> `; };

Aici avem trei funcții. Prima este funcția pe care am văzut-o mai devreme pentru calcularea celui de-al n-lea FN. A doua funcție este doar o funcție de utilitate pentru a atașa un sufix adecvat unui număr întreg. A treia funcție preia câteva argumente și scoate un marcaj pe care îl vom introduce ulterior în DOM. Primul argument este numărul al cărui FN este calculat. Al doilea argument este FN calculat. Ultimul argument este timpul necesar pentru a efectua calculul.

Încă în src/index.js , adăugați codul de mai jos chiar sub cel anterior.

 const errPar = document.getElementById("error"); const btn = document.getElementById("submit-btn"); const input = document.getElementById("number-input"); const resultsContainer = document.getElementById("results-container"); btn.addEventListener("click", (e) => { errPar.textContent = ''; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const startTime = new Date().getTime(); const sum = fib(num); const time = new Date().getTime() - startTime; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, sum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); });

În primul rând, folosim document API pentru a obține nodurile DOM din fișierul nostru HTML. Primim o referință la paragraful în care vom afișa mesaje de eroare; Intrarea; butonul de calcul și containerul în care vom afișa rezultatele noastre.

Apoi, atașăm butonul de gestionare a evenimentelor „clic”. Când se face clic pe butonul, luăm orice se află în elementul de intrare și îl convertim într-un număr, dacă obținem ceva mai mic de 2, afișăm un mesaj de eroare și revenim. Dacă obținem un număr mai mare de 2, continuăm. În primul rând, înregistrăm ora curentă. După aceea, calculăm FN. Când se termină, obținem o diferență de timp care reprezintă cât timp a durat calculul. În partea rămasă a codului, creăm un nou div . Apoi setăm HTML-ul său interior să fie rezultatul funcției textCont() pe care am definit-o mai devreme. În cele din urmă, îi adăugăm o clasă (pentru stil) și o anexăm la containerul de rezultate. Efectul acestui lucru este că fiecare calcul va apărea într-un div separat sub cel precedent.

Se afișează numerele Fibonacci calculate până la 43
Unele numere Fibonacci. (Previzualizare mare)

Putem vedea că pe măsură ce numărul crește, și timpul de calcul crește (exponențial). De exemplu, de la 30 la 35, timpul de calcul a crescut de la 13 ms la 130 ms. Putem considera totuși acele operațiuni ca fiind „rapide”. La 40 vedem un timp de calcul de peste 1 secundă. Pe computerul meu, aici încep să observ că pagina nu mai răspunde. În acest moment, nu mai pot interacționa cu pagina în timp ce calculul este în desfășurare. Nu pot să mă concentrez pe intrare sau să fac altceva.

Vă amintiți când am vorbit despre JavaScript cu un singur thread? Ei bine, acel fir a fost „blocat” de acest calcul de lungă durată, așa că orice altceva trebuie să „aștepte” să se termine. Poate începe de la o valoare mai mică sau mai mare pe mașina dvs., dar veți ajunge la acel punct. Observați că a fost nevoie de aproape 10 secunde pentru a calcula valoarea lui 44. Dacă au fost alte lucruri de făcut în aplicația dvs. web, ei bine, utilizatorul trebuie să aștepte ca Fib(44) să se termine înainte de a putea continua. Dar dacă ați desfășurat un lucrător web pentru a se ocupa de acest calcul, utilizatorii dvs. ar putea continua cu altceva în timp ce acesta rulează.

Să vedem acum cum lucrătorii web ne ajută să depășim această problemă.

Un exemplu de lucrător web în acțiune

În această secțiune, vom delega sarcina de a calcula al-lea FN unui lucrător web. Acest lucru va ajuta la eliberarea firului principal și la menținerea UI receptivă în timp ce calculul este în desfășurare.

A începe cu lucrătorii web este surprinzător de simplu. Să vedem cum. Creați un fișier nou src/fib-worker.js . și introduceți următorul cod.

 const fib = (n) => (n < 2 ? n : fib(n - 1) + fib(n - 2)); onmessage = (e) => { const { num } = e.data; const startTime = new Date().getTime(); const fibNum = fib(num); postMessage({ fibNum, time: new Date().getTime() - startTime, }); };

Observați că am mutat funcția care calculează al n-lea număr Fibonacci, fib în interiorul acestui fișier. Acest fișier va fi rulat de lucrătorul nostru web.

Amintiți-vă în secțiunea Ce este un lucrător web , am menționat că lucrătorii web și părintele lor comunică folosind handlerul de evenimente onmessage și metoda postMessage() . Aici folosim handlerul de evenimente onmessage pentru a asculta mesajele din scriptul părinte. Odată ce primim un mesaj, destructuram numărul din atributul de date al evenimentului. În continuare, obținem ora curentă și începem calculul. Odată ce rezultatul este gata, folosim metoda postMessage() pentru a posta rezultatele înapoi în scriptul părinte.

Deschideți src/index.js , să facem câteva modificări.

 ... const worker = new window.Worker("src/fib-worker.js"); btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); }; });

Primul lucru de făcut este să creați lucrătorul web folosind constructorul Worker . Apoi, în ascultătorul de evenimente al butonului nostru, trimitem un număr lucrătorului folosind worker.postMessage({ num }) . După aceea, setăm o funcție pentru a asculta erorile din lucrător. Aici pur și simplu returnăm eroarea. Cu siguranță poți face mai mult dacă vrei, cum ar fi să-l arăți în DOM. Apoi, ascultăm mesajele de la lucrător. Odată ce primim un mesaj, destructuram time și fibNum și continuăm procesul de a le afișa în DOM.

Rețineți că în interiorul lucrătorului web, evenimentul onmessage este disponibil în domeniul de aplicare al lucrătorului, așa că l-am fi putut scrie ca self.onmessage și self.postMessage() . Dar în scriptul părinte, trebuie să le atașăm lucrătorului însuși.

În captura de ecran de mai jos, veți vedea fișierul web worker în fila surse din Chrome Dev Tools. Ceea ce ar trebui să observați este că interfața de utilizare rămâne receptivă, indiferent de numărul pe care îl introduceți. Acest comportament este magia lucrătorilor web.

Vedere a unui fișier activ de lucru web
Un fișier de lucru web care rulează. (Previzualizare mare)

Am făcut multe progrese cu aplicația noastră web. Dar mai putem face ceva pentru a o îmbunătăți. Implementarea noastră actuală folosește un singur lucrător pentru a gestiona fiecare calcul. Dacă apare un mesaj nou în timp ce unul rulează, cel vechi este înlocuit. Pentru a ocoli acest lucru, putem crea un nou lucrător pentru fiecare apel pentru a calcula FN. Să vedem cum să facem asta în secțiunea următoare.

Lucrul cu mai mulți lucrători web

În prezent, gestionăm fiecare cerere cu un singur lucrător. Astfel, o solicitare primită o va înlocui pe una anterioară care încă nu a fost finalizată. Ceea ce ne dorim acum este să facem o mică modificare pentru a genera un nou lucrător web pentru fiecare solicitare. Vom ucide acest muncitor odată ce se va termina.

Deschideți src/index.js și mutați linia care creează lucrătorul web în interiorul handler-ului de evenimente clic al butonului. Acum, handlerul de evenimente ar trebui să arate ca mai jos.

 btn.addEventListener("click", (e) => { errPar.textContent = ""; const num = window.Number(input.value); if (num < 2) { errPar.textContent = "Please enter a number greater than 2"; return; } const worker = new window.Worker("src/fib-worker.js"); // this line has moved inside the event handler worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; const resultDiv = document.createElement("div"); resultDiv.innerHTML = textCont(num, fibNum, time); resultDiv.className = "result-div"; resultsContainer.appendChild(resultDiv); worker.terminate() // this line terminates the worker }; });

Am făcut două modificări.

  1. Am mutat această linie const worker = new window.Worker("src/fib-worker.js") în interiorul handler-ului de evenimente clic al butonului.
  2. Am adăugat această linie worker.terminate() pentru a renunța la lucru după ce am terminat cu ea.

Deci, pentru fiecare clic pe buton, creăm un nou lucrător care să se ocupe de calcul. Astfel, putem continua să schimbăm intrarea și fiecare rezultat va apărea pe ecran odată ce calculul se termină. În captura de ecran de mai jos puteți vedea că valorile pentru 20 și 30 apar înaintea celei de 45. Dar eu am început primul cu 45. Odată ce funcția revine pentru 20 și 30, rezultatele lor au fost postate, iar lucrătorul a încetat. Când totul se termină, nu ar trebui să avem niciun lucrător în fila surse.

arătând numerele Fibonacci cu lucrători terminați
Ilustrație cu mai mulți lucrători independenți. (Previzualizare mare)

Am putea încheia acest articol chiar aici, dar dacă aceasta ar fi o aplicație react, cum am aduce lucrătorii web în ea. Acesta este punctul central al secțiunii următoare.

Lucrătorii web în reacție

Pentru a începe, creați o nouă aplicație de reacție folosind CRA. Copiați fișierul fib-worker.js în folderul public/ al aplicației dvs. react. Punerea fișierului aici rezultă din faptul că aplicațiile React sunt aplicații cu o singură pagină. Cam acesta este singurul lucru specific utilizării lucrătorului într-o aplicație react. Tot ce urmează de aici este React pur.

În folderul src/ creați un fișier helpers.js și exportați funcția ordinal_suffix() din acesta.

 // src/helpers.js export const ordinal_suffix = (num) => { // 1st, 2nd, 3rd, 4th, etc. const j = num % 10; const k = num % 100; switch (true) { case j === 1 && k !== 11: return num + "st"; case j === 2 && k !== 12: return num + "nd"; case j === 3 && k !== 13: return num + "rd"; default: return num + "th"; } };

Aplicația noastră ne va cere să menținem o anumită stare, așa că creați un alt fișier, src/reducer.js și inserați în reductor de stare.

 // src/reducers.js export const reducer = (state = {}, action) => { switch (action.type) { case "SET_ERROR": return { ...state, err: action.err }; case "SET_NUMBER": return { ...state, num: action.num }; case "SET_FIBO": return { ...state, computedFibs: [ ...state.computedFibs, { id: action.id, nth: action.nth, loading: action.loading }, ], }; case "UPDATE_FIBO": { const curr = state.computedFibs.filter((c) => c.id === action.id)[0]; const idx = state.computedFibs.indexOf(curr); curr.loading = false; curr.time = action.time; curr.fibNum = action.fibNum; state.computedFibs[idx] = curr; return { ...state }; } default: return state; } };

Să trecem peste fiecare tip de acțiune unul după altul.

  1. SET_ERROR : setează o stare de eroare atunci când este declanșată.
  2. SET_NUMBER : setează valoarea din caseta noastră de introducere la stare.
  3. SET_FIBO : adaugă o nouă intrare la matricea de FN-uri calculate.
  4. UPDATE_FIBO : aici căutăm o anumită intrare și o înlocuim cu un nou obiect care are FN-ul calculat și timpul necesar pentru ao calcula.

Vom folosi acest reductor în curând. Înainte de asta, să creăm componenta care va afișa FN-urile calculate. Creați un fișier nou src/Results.js și inserați codul de mai jos.

 // src/Results.js import React from "react"; export const Results = (props) => { const { results } = props; return ( <div className="results-container"> {results.map((fb) => { const { id, nth, time, fibNum, loading } = fb; return ( <div key={id} className="result-div"> {loading ? ( <p> Calculating the{" "} <span className="bold"> {nth} </span>{" "} Fibonacci number... </p> ) : ( <> <p> Time: <span className="bold">{time} ms</span> </p> <p> <span className="bold"> {nth} </span>{" "} fibonnaci number:{" "} <span className="bold"> {fibNum} </span> </p> </> )} </div> ); })} </div> ); };

Cu această modificare, începem procesul de conversie a fișierului nostru anterior index.html în jsx. Acest fișier are o singură responsabilitate: luați o serie de obiecte reprezentând FN-uri calculate și afișați-le. Singura diferență față de ceea ce aveam înainte este introducerea unei stări de încărcare . Deci, acum, când calculul rulează, arătăm starea de încărcare pentru a informa utilizatorul că se întâmplă ceva.

Să punem piesele finale prin actualizarea codului din src/App.js . Codul este destul de lung, așa că îl vom face în doi pași. Să adăugăm primul bloc de cod.

 import React from "react"; import "./App.css"; import { ordinal_suffix } from "./helpers"; import { reducer } from './reducer' import { Results } from "./Results"; function App() { const [info, dispatch] = React.useReducer(reducer, { err: "", num: "", computedFibs: [], }); const runWorker = (num, id) => { dispatch({ type: "SET_ERROR", err: "" }); const worker = new window.Worker('./fib-worker.js') worker.postMessage({ num }); worker.onerror = (err) => err; worker.onmessage = (e) => { const { time, fibNum } = e.data; dispatch({ type: "UPDATE_FIBO", id, time, fibNum, }); worker.terminate(); }; }; return ( <div> <div className="heading-container"> <h1>Computing the nth Fibonnaci number</h1> </div> <div className="body-container"> <p className="error"> {info.err} </p> // ... next block of code goes here ... // <Results results={info.computedFibs} /> </div> </div> ); } export default App;

Ca de obicei, aducem importurile noastre. Apoi instanțiem o funcție de stare și actualizare cu cârligul useReducer. Definim apoi o funcție, runWorker() , care ia un număr și un ID și stabilește apelarea unui lucrător web pentru a calcula FN-ul pentru acel număr.

Rețineți că pentru a crea lucrătorul, trecem o cale relativă către constructorul de lucru. În timpul execuției, codul nostru React este atașat la fișierul public/index.html , astfel încât poate găsi fișierul fib-worker.js în același director. Când calculul se termină (declanșat de worker.onmessage ), acțiunea UPDATE_FIBO este trimisă, iar lucrătorul se încheie ulterior. Ceea ce avem acum nu este foarte diferit de ceea ce aveam anterior.

În blocul return al acestei componente, redăm același HTML pe care îl aveam înainte. De asemenea, trecem matricea de numere calculate la componenta <Results /> pentru randare.

Să adăugăm blocul final de cod în interiorul instrucțiunii return .

 <div className="input-div"> <input type="number" value={info.num} className="number-input" placeholder="Enter a number" onChange={(e) => dispatch({ type: "SET_NUMBER", num: window.Number(e.target.value), }) } /> <button className="btn-submit" onClick={() => { if (info.num < 2) { dispatch({ type: "SET_ERROR", err: "Please enter a number greater than 2", }); return; } const id = info.computedFibs.length; dispatch({ type: "SET_FIBO", id, loading: true, nth: ordinal_suffix(info.num), }); runWorker(info.num, id); }} > Calculate </button> </div>

Am setat un handler onChange pe intrare pentru a actualiza variabila de stare info.num . Pe buton, definim un handler de evenimente onClick . Când se face clic pe butonul, verificăm dacă numărul este mai mare de 2. Observați că înainte de a apela runWorker() , trimitem mai întâi o acțiune pentru a adăuga o intrare la matricea de FN-uri calculate. Această intrare va fi actualizată odată ce lucrătorul își încheie munca. În acest fel, fiecare intrare își menține poziția în listă, spre deosebire de ceea ce aveam înainte.

În cele din urmă, copiați conținutul styles.css de înainte și înlocuiți conținutul App.css .

Acum avem totul la loc. Acum porniți serverul de reactie și jucați-vă cu câteva numere. Luați notă de starea de încărcare, care este o îmbunătățire a UX. De asemenea, rețineți că interfața de utilizare rămâne receptivă chiar și atunci când introduceți un număr de până la 1000 și faceți clic pe „Calculați”.

afișează starea de încărcare în timp ce lucrătorul este activ.
Se afișează starea de încărcare și lucrătorul web activ. (Previzualizare mare)

Observați starea de încărcare și lucrătorul activ. Odată ce a 46-a valoare este calculată, lucrătorul este ucis și starea de încărcare este înlocuită cu rezultatul final.

  • Codul sursă pentru această aplicație React este disponibil pe Github și există o aplicație găzduită pe vercel.

Concluzie

Pf! A fost o călătorie lungă, așa că hai să o încheiem. Vă încurajez să aruncați o privire la intrarea MDN pentru lucrătorii web (consultați lista de resurse de mai jos) pentru a afla alte moduri de utilizare a lucrătorilor web.

În acest articol, am aflat despre ce sunt lucrătorii web și despre felul de probleme pe care trebuie să le rezolve. De asemenea, am văzut cum să le implementăm folosind JavaScript simplu. În cele din urmă, am văzut cum să implementăm lucrătorii web într-o aplicație React.

Vă încurajez să profitați de acest excelent API pentru a oferi o experiență mai bună pentru utilizatorii dvs.

Resurse suplimentare

  • Console.time() , documente web MDN
  • {JSON} Substituent, site oficial
  • Utilizarea lucrătorilor web, documente web MDN
  • Numărul Fibonacci, Wikipedia
  • Operator condiționat (ternar), documente web MDN
  • Document , API-uri web, documente web MDN
  • Noțiuni introductive, creați aplicația React (docs)
  • Function.prototype.toString() , documente web MDN
  • Documente web IIFE, MDN
  • workerSetup.js , Tutoriale minunate Fullstack, GitHub
  • „Programare paralelă în JavaScript utilizând lucrători web”, Uday Hiwarale, Medium