Asinkroni JavaScript: Objašnjeni povratni pozivi i obećanja

Suočavanje s asinkronim kodom, što znači da se kod koji se ne izvršava odmah poput web zahtjeva ili odbrojavanja, može biti nezgodno. JavaScript nam pruža dva načina za rukovanje asinkronim ponašanjem: povratni pozivi i obećanja.

Povratni pozivi bili su jedini izvorno podržani način za rješavanje async koda do 2016. godine, kada je Promiseobjekt uveden u jezik. Međutim, programeri JavaScript-a sami su implementirali slične funkcije prije nego što su obećanja stigla na scenu. Pogledajmo neke razlike između povratnih poziva i obećanja i vidjet ćemo kako se nosimo s koordinacijom više obećanja.

Asinkrone funkcije koje koriste povratne pozive uzimaju funkciju kao parametar, koja će se pozvati nakon završetka rada. Ako ste koristili nešto poput setTimeoutpreglednika, koristili ste povratne pozive.

// Povratni poziv možete definirati zasebno ...

neka myCallback = () => {

  console.log ('Pozvan!');

};

setTimeout (myCallback, 3000);

// ... ali također je uobičajeno vidjeti povratne pozive definirane u redu

setTimeout (() => {

  console.log ('Pozvan!');

}, 3000);

Obično funkcija koja uzima povratni poziv uzima ga kao posljednji argument. To nije slučaj gore, pa se pretvarajmo da se zove nova funkcija waitkoja je slična, setTimeoutali uzima prva dva argumenta u suprotnom redoslijedu:

// Koristili bismo našu novu funkciju ovako:

waitCallback (3000, () => {

  console.log ('Pozvan!');

});

Ugniježđeni povratni pozivi i piramida propasti

Povratni pozivi izvrsno funkcioniraju za rukovanje asinkronim kodom, ali postaju nezgodni kad počnete koordinirati više asinkronih funkcija. Na primjer, ako smo htjeli pričekati dvije sekunde i prijaviti nešto, zatim pričekati tri sekunde i prijaviti nešto drugo, a zatim pričekati četiri sekunde i prijaviti nešto drugo, naša sintaksa postaje duboko ugniježđena.

// Koristili bismo našu novu funkciju ovako:

waitCallback (2000, () => {

  console.log ('Prvi povratni poziv!');

  waitCallback (3000, () => {

    console.log ('Drugi povratni poziv!');

    waitCallback (4000, () => {

      console.log ('Treći povratni poziv!');

    });

  });

});

Ovo se može činiti trivijalnim primjerom (i jest), ali nije rijetkost davati nekoliko web zahtjeva zaredom na temelju rezultata vraćanja prethodnog zahtjeva. Ako vaša AJAX knjižnica koristi povratne pozive, vidjet ćete kako se gornja struktura igra. U primjerima koji su dublje ugniježđeni, vidjet ćete ono što se naziva piramidom propasti, koja svoje ime dobiva po obliku piramide izrađenom u uvučenom razmaku na početku redaka.

Kao što vidite, naš kôd postaje strukturno iskrivljen i teže ga je čitati kada se radi o asinkronim funkcijama koje se trebaju odvijati uzastopno. Ali postaje još zamršenije. Zamislite ako bismo željeli pokrenuti tri ili četiri web zahtjeva i izvršiti neki zadatak tek nakon što se svi vrate. Potičem vas da pokušate to učiniti ako prije niste naišli na izazov.

Lakša asinkronizacija uz obećanja

Obećanja pružaju fleksibilniji API za rješavanje asinkronih zadataka. Zahtijeva da funkcija bude napisana tako da vraća Promiseobjekt koji ima neke standardne značajke za rukovanje naknadnim ponašanjem i koordinaciju više obećanja. Da se naša waitCallbackfunkcija Promisetemeljila, trebao bi samo jedan argument, a to su milisekunde čekanja. Svaka sljedeća funkcionalnost bila bi vezana uz obećanje. Naš bi prvi primjer izgledao ovako:

neka myHandler = () => {

  console.log ('Pozvan!');

};

waitPromise (3000). then (myHandler);

U gornjem primjeru waitPromise(3000)vraća Promiseobjekt koji ima neke metode koje možemo koristiti, kao što je then. Ako bismo htjeli izvršavati nekoliko asinkronih funkcija jednu za drugom, mogli bismo izbjeći piramidu propasti koristeći obećanja. Taj kôd, prepisan kao podrška našem novom obećanju, izgledao bi ovako:

// Bez obzira koliko sekvencijalnih asinkronih zadataka imamo, nikad ne pravimo piramidu.

waitPromise (2000.)

  .potom (() => {

    console.log ('Prvi povratni poziv!');

    povratak waitPromise (3000);

  })

  .potom (() => {

    console.log ('Drugi povratni poziv!');

    povratak waitPromise (4000);

  })

  .potom (() => {

    console.log ('Drugi povratni poziv!');

    povratak waitPromise (4000);

  });

Još bolje, ako bismo trebali koordinirati asinkrone zadatke koji podržavaju Obećanja, mogli bismo koristiti all, što je statična metoda na Promiseobjektu koja uzima nekoliko obećanja i kombinira ih u jedno. To bi izgledalo ovako:

Promise.all ([

  waitPromise (2000),

  waitPromise (3000),

  waitPromise (4000)

]). then (() => console.log ('Sve je gotovo!'));

Sljedeći ćemo tjedan istražiti kako obećanja funkcioniraju i kako ih idiomatski koristiti. Ako samo učite JavaScript ili ste zainteresirani za testiranje svog znanja, pokušajte waitCallbackili pokušajte postići ekvivalent Promise.allpovratnim pozivima.

Kao i uvijek, obratite mi se na Twitteru s bilo kojim komentarom ili pitanjem.