Există o mulțime de vorbe despre programarea asincronă, dar ce anume este marele lucru? Marea afacere este că dorim ca codul nostru să nu fie blocat.
Sarcinile care pot bloca aplicația noastră includ efectuarea de solicitări HTTP, interogarea unei baze de date sau deschiderea unui fișier. Unele limbi, cum ar fi Java, se ocupă de acest lucru prin crearea de fire multiple. Cu toate acestea, JavaScript are un singur fir, deci trebuie să proiectăm programele noastre astfel încât nici o sarcină să nu blocheze fluxul.
Programarea asincronă rezolvă această problemă. Ne permite să executăm sarcini mai târziu, astfel încât să nu ținem întreaga programare în așteptarea finalizării sarcinilor. Ajută, de asemenea, când vrem să asigurăm executarea secvențială a sarcinilor.
În prima parte a acestui tutorial, vom învăța conceptele din spatele codului sincron și asincron și vom vedea cum putem folosi funcțiile callback pentru a rezolva problemele cu asincronie.
Vreau să vă aduceți aminte de ultima oară când ați mers la cumpărături la magazinul alimentar. Probabil au fost deschise mai multe case de marcat pentru a verifica clienții. Acest lucru ajută procesul de stocare a mai multor tranzacții în același timp. Acesta este un exemplu de concurrency.
Pentru a pune pur și simplu, concurrency face mai mult de o sarcină, în același timp. Sistemul dvs. de operare este concurent deoarece execută simultan mai multe procese. Un proces este un mediu de execuție sau o instanță a unei aplicații care rulează. De exemplu, browserul, editorul de text și programele antivirus sunt toate procesele de pe computer care rulează simultan.
Aplicațiile pot fi, de asemenea, concurentă. Acest lucru este realizat cu fire. Nu voi fi prea adânc, deoarece acest lucru depășește domeniul de aplicare al acestui articol. Dacă doriți o explicație detaliată a modului în care funcționează JavaScript sub capota, vă recomand să vizionați acest videoclip.
Un fir este o unitate în cadrul unui proces care execută un cod. În exemplul magazinului nostru, fiecare linie de plată va fi un fir. Dacă în magazin avem doar o singură linie de plată, acest lucru ar schimba modul în care am procesat clienții.
Ați fost vreodată în linie și ați avut ceva deținut de tranzacția dvs.? Poate că ai nevoie de o verificare a prețurilor sau a trebuit să vezi un manager. Când sunt la oficiul poștal, încercând să livrez un pachet și nu am completat etichetele, casierul mă întreabă să plec deoparte, în timp ce continuă să verifice alți clienți. Când sunt gata, mă întorc în fața liniei pentru a fi verificată.
Acest lucru este similar cu modul în care funcționează programarea asincronă. Casierul ar fi putut să mă aștepte. Uneori o fac. Dar este o experiență mai bună să nu țineți linia și să verificați ceilalți clienți. Ideea este că clienții nu trebuie să fie verificați în ordinea în care sunt în linie. De asemenea, codul nu trebuie executat în ordinea în care îl scriem.
Este firesc să ne gândim la codul nostru executând secvențial de sus în jos. Aceasta este sincronă. Cu toate acestea, cu JavaScript, unele sarcini sunt în mod inerent asincrone (de exemplu, setTimeout), iar unele sarcini pe care le proiectăm pentru a fi asincrone, deoarece știm că înainte de timp pot fi blocate.
Să aruncăm o privire la un exemplu practic folosind fișierele din Node.js. Dacă doriți să încercați exemplele de cod și aveți nevoie de un primer pentru utilizarea Node.js, puteți găsi instrucțiuni de pornire din acest tutorial. În acest exemplu, vom deschide un fișier de postări și vom prelua unul dintre postări. Apoi vom deschide un fișier de comentarii și vom prelua comentariile pentru postarea respectivă.
Acesta este modul sincron:
const fs = necesită ("fs"); const path = necesită ('path'); const posturiUrl = path.join (__ dirname, 'db / posts.json'); const constUrl = path.join (__ dirname, 'db / comments.json'); // returnați datele din funcția noastră de fișier loadCollection (url) try const response = fs.readFileSync (url, 'utf8'); returnați JSON.parse (răspuns); captură (eroare) console.log (eroare); // returnați un obiect prin funcția id getRecord (colecție, id) return colecție.find (funcție (element) return element.id == id;); // returnează o serie de comentarii pentru o funcție post getCommentsByPost (comments, postId) returnează comments.filter (funcția (comment) return comment.postId == postId;); // cod inițializare const posts = loadCollection (postsUrl); const post = getRecord (posturi, "001"); const comments = loadCollection (commentsUrl); const postComments = getCommentsByPost (comentarii, post.id); console.log (post); console.log (postComments);
"id": "001", "titlu": "Salut", "text": "Hello World", "autor": "Jane Doe", "id": "002"; "JavaScript 101", "text": "Fundamentele programării", "autor": "Alberta Williams", "id": "003" Callbacks, Promises și Async / Await. "," Autor ":" Alberta Williams "]
["id": "phx732", "postId": "003", "text": "Nu primesc chestiile de apel invers". , "id": "avj9438", "postId": "003", "text": "Aceasta este o informație foarte utilă". , "id": "gnk368", "postId": "001", "text": "Acesta este un comentariu de test". ]
readFileSync
metoda deschide fișierul sincron. Prin urmare, putem scrie și codul nostru de inițializare într-un mod sincron. Dar aceasta nu este cea mai bună cale de a deschide fișierul, deoarece aceasta este o sarcină potențial de blocare. Deschiderea fișierului trebuie făcută în mod asincron, astfel încât fluxul de execuție să fie continuu.
Nodul are a readfile
metoda pe care o putem folosi pentru a deschide fișierul asincron. Aceasta este sintaxa:
fs.readFile (url, 'utf8', funcție (eroare, date) ...);
S-ar putea fi tentat să ne întoarcem datele în cadrul acestei funcții de apel invers, dar nu va fi disponibil pentru noi în interiorul nostru loadCollection
funcţie. Codul de inițializare va trebui să se schimbe și pentru că nu vom avea valorile corecte pentru a atribui variabilelor noastre.
Pentru a ilustra problema, să examinăm un exemplu mai simplu. Ce credeți că va imprima următorul cod?
funcția task1 () setTimeout (funcția () console.log ('prima');, 0); funcția task2 () console.log ('second'); funcția task3 () console.log ("a treia"); sarcina 1(); task2 (); task3 ();
Acest exemplu va tipări "al doilea", "al treilea" și apoi "primul". Nu contează că setTimeout
funcția are o întârziere de 0. Este o sarcină asincronă în JavaScript, deci întotdeauna va fi amânată executarea ulterioară. firstTask
funcția poate reprezenta orice sarcină asincronă, cum ar fi deschiderea unui fișier sau interogarea bazei noastre de date.
O soluție pentru a ne îndeplini sarcinile în ordinea dorită este utilizarea funcțiilor de apel invers.
Pentru a utiliza funcțiile de retur, treceți o funcție ca parametru la o altă funcție și apoi apelați funcția atunci când sarcina a fost terminată. Dacă aveți nevoie de un primer pentru utilizarea funcțiilor de ordin superior, reactivex are un tutorial interactiv pe care îl puteți încerca.
Comenzile de returnare ne permit să forțeze executarea sarcinilor secvențiale. De asemenea, ele ne ajută atunci când avem sarcini care depind de rezultatele unei sarcini anterioare. Folosind callbacks, putem rezolva ultimul exemplu astfel încât să se imprime "first", "second" și apoi "third".
(cb) setTimeout (funcția () return cb ('prima');, 0); funcția secunde (cb) return cb ('secundă'); funcția a treia (cb) return cb ("a treia"); primul (funcția (rezultatul1) console.log (rezultatul1); al doilea (funcția (rezultatul2) console.log (rezultatul2); );
În loc să tipărim șirul în interiorul fiecărei funcții, returnează valoarea din interiorul unui apel invers. Când codul nostru este executat, vom imprima valoarea care a fost transmisă în apelul nostru de apel. Asta este ceea ce noi readfile
funcții.
Revenind la exemplul nostru de fișiere, ne putem schimba loadCollection
astfel încât să utilizeze callback-uri pentru a citi fișierul într-o manieră asincronă.
funcția loadCollection (url, callback) fs.readFile (url, 'utf8', funcția (eroare, date) if (eroare) console.log (eroare); ;);
Și acesta este modul în care codul de inițializare va arăta folosind callback-urile:
loadCollection (postsUrl, funcția (posturi) loadCollection (commentsUrl, funcția (comentarii) getRecord (posturi, "001", funcția (post) const postComments = getCommentsByPost (comments; console.log (postComments););););
Un lucru de observat în nostru loadCollection
funcția este că în loc de a utiliza încearcă să prinzi
declarație pentru a gestiona erorile, vom folosi un dacă / altceva
afirmație. Blocul de captură nu ar putea detecta erorile returnate de la readfile
suna inapoi.
Este o practică bună să avem erori de manipulare a erorilor în codul nostru pentru erorile care sunt rezultatul influențelor externe, spre deosebire de programele de bug-uri. Aceasta include accesarea fișierelor, conectarea la o bază de date sau efectuarea unei solicitări HTTP.
În exemplul de cod revizuit, nu am inclus nicio manipulare a erorilor. Dacă ar apărea o eroare la oricare dintre pași, programul nu va continua. Ar fi frumos să oferi instrucțiuni semnificative.
Un exemplu în care manipularea erorilor este importantă este dacă avem o sarcină pentru a vă conecta la un utilizator. Aceasta implică obținerea unui nume de utilizator și a unei parole dintr-un formular, interogarea bazei noastre de date pentru a vedea dacă este o combinație validă și apoi redirecționarea utilizatorului spre tabloul de bord dacă reușim. Dacă numele de utilizator și parola erau nevalide, aplicația noastră nu mai funcționa dacă nu îi spunem ce să facem.
O experiență mai bună a utilizatorului ar fi să returneze un mesaj de eroare utilizatorului și să îi permită să reîncarce logarea. În exemplul nostru de fișiere, am putea trece un obiect de eroare de-a lungul funcției de apel invers. Acesta este cazul cu readfile
funcţie. Atunci când executăm codul, putem adăuga un cod dacă / altceva
declarație pentru a rezolva rezultatul reușit și rezultatul respins.
Utilizând metoda de apel invers, scrieți un program care va deschide un fișier de utilizatori, selectați un utilizator și apoi deschideți un fișier de postări și tipăriți informațiile utilizatorului și toate postările acestuia.
Programarea asincronă este o metodă utilizată în codul nostru pentru a amâna evenimentele pentru o execuție ulterioară. Când aveți de-a face cu o sarcină asincronă, callback-urile sunt o soluție pentru sincronizarea sarcinilor noastre, astfel încât acestea să fie executate secvențial.
Dacă avem mai multe sarcini care depind de rezultatul sarcinilor anterioare, o soluție este utilizarea mai multor inversări inversate. Cu toate acestea, acest lucru ar putea duce la o problemă cunoscută sub numele de "callback iad". Promisiunile rezolvă problema cu iadul de apel și funcțiile de asincron permiteți-ne să scriem codul nostru în mod sincron. În partea a doua a acestui tutorial, vom afla ce sunt și cum să le folosim în codul nostru.