async / await ne-a eliberat de callback hell, dar oamenii au început să abuzeze de el – ducând la nașterea async / await hell.

În acest articol, voi încerca să explic ce este asincronizarea / așteptarea iadului și voi împărtăși și câteva sfaturi pentru a scăpa de el.

Ce este asincronizarea / așteaptă iadul

În timp ce lucrează cu JavaScript asincron, oamenii scriu adesea mai multe declarații una după alta și dau o palmă asteapta înainte de un apel de funcție. Acest lucru cauzează probleme de performanță, deoarece de multe ori o afirmație nu depinde de cea anterioară – dar trebuie totuși să așteptați finalizarea celei anterioare.

Un exemplu de asincronizare / așteaptă iadul

Luați în considerare dacă ați scris un scenariu pentru a comanda o pizza și o băutură. Scriptul ar putea arăta astfel:

La suprafață arată corect și funcționează. Dar aceasta nu este o implementare bună, deoarece lasă concurența în afara imaginii. Să înțelegem ce face, astfel încât să putem rezolva problema.

Explicaţie

Am înfășurat codul nostru într-o sincronizare IIFE. Următoarele se întâmplă în această ordine exactă:

  1. Obțineți lista de pizza.
  2. Obțineți lista de băuturi.
  3. Alegeți o pizza din listă.
  4. Alegeți o băutură din listă.
  5. Adăugați pizza aleasă în coș.
  6. Adăugați băutura aleasă în coș.
  7. Comandați articolele din coș.

Deci, ce e în neregulă?

După cum am subliniat mai devreme, toate aceste declarații se execută una câte una. Aici nu există concurență. Gândiți-vă bine: de ce așteptăm să obținem lista de pizza înainte de a încerca să obținem lista de băuturi? Ar trebui doar să încercăm să reunim ambele liste. Cu toate acestea, atunci când trebuie să alegem o pizza, trebuie să avem în prealabil lista de pizza. Același lucru este valabil și pentru băuturi.

Așadar, putem concluziona că munca legată de pizza și munca legată de băuturi se pot întâmpla în paralel, dar pașii individuali implicați în munca legată de pizza trebuie să aibă loc secvențial (unul câte unul).

Un alt exemplu de implementare defectuoasă

Acest fragment JavaScript va primi articolele din coș și va solicita să le comandați.

async function orderItems() {
  const items = await getCartItems()    // async call
  const noOfItems = items.length
  for(var i = 0; i < noOfItems; i++) {
    await sendRequest(items[i])    // async call
  }
}

În acest caz, bucla for trebuie să aștepte fișierul sendRequest() funcția de finalizat înainte de a continua următoarea iterație. Cu toate acestea, nu trebuie să așteptăm. Dorim să trimitem toate cererile cât mai repede posibil și apoi putem aștepta ca toate să fie finalizate.

Sper că acum vă apropiați mai mult de înțelegerea a ceea ce este asincronizat / așteptați iadul și cât de grav afectează performanța programului dvs. Acum vreau să vă pun o întrebare.

Ce se întâmplă dacă uităm cuvântul cheie așteptat?

Dacă uitați să utilizați asteapta în timp ce apelați o funcție asincronă, funcția începe să se execute. Aceasta înseamnă că await nu este necesar pentru executarea funcției. Funcția de sincronizare va returna o promisiune, pe care o puteți folosi mai târziu.

(async () => {
  const value = doSomeAsyncTask()
  console.log(value) // an unresolved promise
})()

O altă consecință este că compilatorul nu va ști că doriți să așteptați ca funcția să se execute complet. Astfel, compilatorul va ieși din program fără a finaliza sarcina asincronizată. Deci avem nevoie de asteapta cuvânt cheie.

(async () => {
  const promise = doSomeAsyncTask()
  const value = await promise
  console.log(value) // the actual value
})()

O proprietate interesantă a promisiunilor este că puteți obține o promisiune într-o singură linie și așteptați ca aceasta să se rezolve în alta. Aceasta este cheia evadării asincronizate / așteptării iadului.

După cum puteți vedea, doSomeAsyncTask() întoarce o promisiune. In acest punct doSomeAsyncTask() și-a început execuția. Pentru a obține valoarea rezolvată a promisiunii, folosim cuvântul cheie await și care îi va spune JavaScript să nu execute imediat următoarea linie, ci să așteptăm să se rezolve promisiunea și apoi să executăm următoarea linie.

Cum să ieși din asincronizare / să aștepți iadul?

Ar trebui să urmați acești pași pentru a scăpa de asincronizare / așteptarea iadului.

Găsiți instrucțiuni care depind de executarea altor instrucțiuni

În primul nostru exemplu, selectam o pizza și o băutură. Am ajuns la concluzia că, înainte de a alege o pizza, trebuie să avem lista de pizza. Și înainte de a adăuga pizza în coș, ar trebui să alegem o pizza. Deci, putem spune că acești trei pași depind unul de celălalt. Nu putem face un lucru până nu am terminat lucrul anterior.

Dar dacă o privim mai larg, descoperim că selectarea unei pizza nu depinde de selectarea unei băuturi, deci le putem selecta în paralel. Acesta este un lucru pe care mașinile îl pot face mai bine decât noi.

Astfel am descoperit unele afirmații care depind de executarea altor afirmații și unele care nu.

Instrucțiuni dependente de grup în funcții asincronizate

După cum am văzut, selectarea pizza implică afirmații dependente, cum ar fi obținerea listei de pizza, alegerea uneia și apoi adăugarea pizza aleasă în coș. Ar trebui să grupăm aceste afirmații într-o funcție asincronă. Astfel obținem două funcții asincronizate, selectPizza() și selectDrink() .

Executați simultan aceste funcții asincronizate

Profităm apoi de bucla evenimentului pentru a rula simultan aceste funcții asincronizate fără blocare. Două modele comune de a face acest lucru sunt revenirea promisiunilor devreme si Promiteți.toate metoda.

Să reparăm exemplele

Urmând cei trei pași, să le aplicăm pe exemplele noastre.

async function selectPizza() {
  const pizzaData = await getPizzaData()    // async call
  const chosenPizza = choosePizza()    // sync call
  await addPizzaToCart(chosenPizza)    // async call
}

async function selectDrink() {
  const drinkData = await getDrinkData()    // async call
  const chosenDrink = chooseDrink()    // sync call
  await addDrinkToCart(chosenDrink)    // async call
}

(async () => {
  const pizzaPromise = selectPizza()
  const drinkPromise = selectDrink()
  await pizzaPromise
  await drinkPromise
  orderItems()    // async call
})()

// Although I prefer it this way 

Promise.all([selectPizza(), selectDrink()]).then(orderItems)   // async call

Acum am grupat afirmațiile în două funcții. În interiorul funcției, fiecare instrucțiune depinde de execuția celei anterioare. Apoi executăm simultan ambele funcții selectPizza() și selectDrink() .

În al doilea exemplu, trebuie să ne confruntăm cu un număr necunoscut de promisiuni. Abordarea acestei situații este foarte ușoară: noi doar creăm o matrice și promovăm promisiunile. Apoi folosind Promise.all() simultan așteptăm ca toate promisiunile să se rezolve.

async function orderItems() {
  const items = await getCartItems()    // async call
  const noOfItems = items.length
  const promises = []
  for(var i = 0; i < noOfItems; i++) {
    const orderPromise = sendRequest(items[i])    // async call
    promises.push(orderPromise)    // sync call
  }
  await Promise.all(promises)    // async call
}

// Although I prefer it this way 

async function orderItems() {
  const items = await getCartItems()    // async call
  const promises = items.map((item) => sendRequest(item))
  await Promise.all(promises)    // async call
}

Sper că acest articol te-a ajutat să vezi dincolo de elementele de bază ale asincronizării / așteptării și, de asemenea, te-a ajutat să îmbunătățești performanța aplicației tale.

Dacă ți-a plăcut articolul, te rog bate din suflet. Sfat – Puteți bate din palme de 50 de ori!

Vă rugăm să distribuiți și pe Fb și Twitter. Dacă doriți să primiți actualizări, urmați-mă Stare de nervozitate și Mediu sau abonați-vă la buletinul meu informativ! Dacă ceva nu este clar sau doriți să indicați ceva, vă rugăm să comentați mai jos.