Kā tikt galā ar ligzdotajiem atzvaniem un izvairīties no “atzvanīšanas elles”

JavaScript ir dīvaina valoda. Reizēm jums jātiek galā ar atzvanīšanu, kas ir citā atzvanī, kas ir vēl vienā atzvanīšanā.

Cilvēki šo modi mīļi sauc par atzvanīšanas elli .

Tas kaut kā izskatās šādi:

firstFunction(args, function() { secondFunction(args, function() { thirdFunction(args, function() { // And so on… }); }); });

Tas ir JavaScript jums. Tas ir prātam neaptverami, lai redzētu ligzdotos atzvanus, bet es nedomāju, ka tas ir “elle”. "Elle" var būt pārvaldāma, ja jūs zināt, ko ar to darīt.

Pēc atzvanīšanas

Pieņemu, ka jūs zināt, kas ir atzvanīšana, ja lasāt šo rakstu. Ja to nedarāt, pirms turpināšanas, lūdzu, izlasiet šo rakstu, lai uzzinātu par atzvanīšanu. Tur mēs runājam par to, kas ir atzvanīšana un kāpēc jūs tos izmantojat JavaScript.

Risinājumi elles atzvanīšanai

Zvanīšanas ellē ir četri risinājumi:

  1. Rakstiet komentārus
  2. Sadaliet funkcijas mazākās funkcijās
  3. Izmantojot solījumus
  4. Izmantojot Async / await

Pirms ienirstam risinājumos, kopā izveidosim atzvanīšanas elli. Kāpēc? Jo tas ir pārāk abstrakts, lai redzētu firstFunction, secondFunctionun thirdFunction. Mēs vēlamies to padarīt konkrētu.

Zvanīšanas elles konstruēšana

Iedomāsimies, ka mēģinām pagatavot burgeru. Lai pagatavotu burgeru, mums jāveic šādas darbības:

  1. Iegūstiet sastāvdaļas (mēs pieņemsim, ka tas ir liellopa gaļas burgers)
  2. Pagatavojiet liellopu gaļu
  3. Iegūstiet burgeru maizītes
  4. Ielieciet vārītu liellopa gaļu starp maizītēm
  5. Pasniedz burgeru

Ja šīs darbības ir sinhronas, jūs apskatīsit funkciju, kas līdzinās šim:

const makeBurger = () => { const beef = getBeef(); const patty = cookBeef(beef); const buns = getBuns(); const burger = putBeefBetweenBuns(buns, beef); return burger; }; const burger = makeBurger(); serve(burger);

Tomēr, pieņemsim, ka mūsu scenārijā mēs paši nevaram pagatavot burgeru. Mums jāsniedz palīgam norādījumi par soļiem, lai pagatavotu burgeru. Pēc tam, kad mēs esam norādījuši palīgu, mums ir jāgaida , līdz palīgs beidz darbu, pirms sākam nākamo soli.

Ja mēs vēlamies kaut ko gaidīt JavaScript, mums jāizmanto atzvanīšana. Lai pagatavotu burgeru, mums vispirms jāsaņem liellopa gaļa. Liellopu gaļu mēs varam pagatavot tikai pēc tam, kad mēs to esam ieguvuši.

const makeBurger = () => { getBeef(function(beef) { // We can only cook beef after we get it. }); };

Lai pagatavotu liellopu gaļu, mums jāpāriet beefuz cookBeeffunkciju. Pretējā gadījumā nav ko gatavot! Tad mums jāgaida, līdz liellopu gaļa tiek pagatavota.

Kad liellopu gaļa ir pagatavota, mēs iegūstam maizītes.

const makeBurger = () => { getBeef(function(beef) { cookBeef(beef, function(cookedBeef) { getBuns(function(buns) { // Put patty in bun }); }); }); };

Pēc tam, kad mēs esam saņēmuši maizītes, mums jāievieto pīrādziņš starp maizītēm. Šeit izveidojas burgers.

const makeBurger = () => { getBeef(function(beef) { cookBeef(beef, function(cookedBeef) { getBuns(function(buns) { putBeefBetweenBuns(buns, beef, function(burger) { // Serve the burger }); }); }); }); };

Visbeidzot, mēs varam pasniegt burgeru! Bet mēs nevaram atgriezties burgerno tā, makeBurgerjo tas ir asinhrons. Lai pasniegtu burgeru, mums jāpieņem atzvanīšana.

const makeBurger = nextStep => { getBeef(function (beef) { cookBeef(beef, function (cookedBeef) { getBuns(function (buns) { putBeefBetweenBuns(buns, beef, function(burger) { nextStep(burger) }) }) }) }) } // Make and serve the burger makeBurger(function (burger) => { serve(burger) })

(Man bija jautri izdarīt šo atzvana piemēru?).

Pirmais atzvanīšanas elles risinājums: rakstiet komentārus

makeBurgerAtzvanīšanas elle ir viegli saprast. Mēs to varam izlasīt. Tas vienkārši ... neizskatās jauki.

Ja lasāt makeBurgerpirmo reizi, jūs varat domāt: "Kāpēc, pie velna, mums ir vajadzīgi tik daudz atzvanīšanas, lai pagatavotu burgeru? Nav jēgas! ”.

Šādā gadījumā jūs vēlaties atstāt komentārus, lai izskaidrotu savu kodu.

// Makes a burger // makeBurger contains four steps: // 1. Get beef // 2. Cook the beef // 3. Get buns for the burger // 4. Put the cooked beef between the buns // 5. Serve the burger (from the callback) // We use callbacks here because each step is asynchronous. // We have to wait for the helper to complete the one step // before we can start the next step const makeBurger = nextStep => { getBeef(function(beef) { cookBeef(beef, function(cookedBeef) { getBuns(function(buns) { putBeefBetweenBuns(buns, beef, function(burger) { nextStep(burger); }); }); }); }); };

Tagad tā vietā, lai domātu “wtf ?!” ieraugot atzvanīšanas elli, jūs saprotat, kāpēc tā jāraksta šādi.

Otrais izsaukuma elles risinājums: sadaliet atzvanīšanu dažādās funkcijās

Mūsu izsaukuma elles piemērs jau ir tā piemērs. Ļaujiet man jums parādīt soli pa solim obligāto kodu, un jūs redzēsiet, kāpēc.

Par getBeef, mūsu pirmajā atzvanīšanas, mums ir jāiet pie ledusskapja, lai iegūtu liellopu. Virtuvē ir divi ledusskapji. Mums jādodas uz pareizo ledusskapi.

const getBeef = nextStep => { const fridge = leftFright; const beef = getBeefFromFridge(fridge); nextStep(beef); };

Lai pagatavotu liellopa gaļu, mums liellopa gaļa ir jāievieto krāsnī; pagrieziet cepeškrāsni līdz 200 grādiem un pagaidiet divdesmit minūtes.

const cookBeef = (beef, nextStep) => { const workInProgress = putBeefinOven(beef); setTimeout(function() { nextStep(workInProgress); }, 1000 * 60 * 20); };

Tagad iedomājieties, vai jums ir jāraksta katra no šīm darbībām makeBurger... jūs, iespējams, noģībsiet no milzīgā koda daudzuma!

Konkrētu piemēru par atzvanīšanas sadalīšanu mazākās funkcijās varat izlasīt šo mazo sadaļu manā atzvanīšanas rakstā.

Trešais izsaukuma elles risinājums: izmantojiet solījumus

Es pieņemšu, ka jūs zināt, kādi ir solījumi. Ja jums nav, lūdzu, izlasiet šo rakstu.

Solījumi var padarīt piezvanīšanas elli daudz vieglāk pārvaldāmu. Iepriekš redzamā ligzdotā koda vietā jums būs šāds:

const makeBurger = () => { return getBeef() .then(beef => cookBeef(beef)) .then(cookedBeef => getBuns(beef)) .then(bunsAndBeef => putBeefBetweenBuns(bunsAndBeef)); }; // Make and serve burger makeBurger().then(burger => serve(burger));

Ja jūs izmantojat viena argumenta stila priekšrocības ar solījumiem, varat pielāgot iepriekš minēto:

const makeBurger = () => { return getBeef() .then(cookBeef) .then(getBuns) .then(putBeefBetweenBuns); }; // Make and serve burger makeBurger().then(serve);

Daudz vieglāk lasīt un pārvaldīt.

Bet jautājums ir, kā jūs varat pārvērst atzvanīšanas kodu par solījumu balstītu kodu.

Atzvanu pārveidošana par solījumiem

Lai atzvanīšanu pārvērstu par solījumiem, mums katram izsaukumam ir jāizveido jauns solījums. Mēs varam resolveapsolīt, kad atzvanīšana būs veiksmīga. Vai arī mēs varam rejectapsolīt, ja atzvanīšana neizdodas.

const getBeefPromise = _ => { const fridge = leftFright; const beef = getBeefFromFridge(fridge); return new Promise((resolve, reject) => { if (beef) { resolve(beef); } else { reject(new Error(“No more beef!”)); } }); }; const cookBeefPromise = beef => { const workInProgress = putBeefinOven(beef); return new Promise((resolve, reject) => { setTimeout(function() { resolve(workInProgress); }, 1000 * 60 * 20); }); };

Praksē, iespējams, jau jums rakstīs atzvanīšanu. Ja izmantojat mezglu, katrai funkcijai, kurā ir atzvanīšana, ir tāda pati sintakse:

  1. Atzvans būtu pēdējais arguments
  2. Atzvanam vienmēr būs divi argumenti. Un šie argumenti ir vienā secībā. (Vispirms kļūda, kam seko viss, kas jūs interesē).
// The function that’s defined for you const functionName = (arg1, arg2, callback) => { // Do stuff here callback(err, stuff); }; // How you use the function functionName(arg1, arg2, (err, stuff) => { if (err) { console.error(err); } // Do stuff });

Ja jūsu atzvanam ir tāda pati sintakse, varat izmantot tādas bibliotēkas kā ES6 Promisify vai Denodeify (de-node-ify), kas atzvanīs. Ja izmantojat Node v8.0 un jaunākas versijas, varat izmantot util.promisify.

Viņi visi trīs strādā. Jūs varat izvēlēties jebkuru bibliotēku, ar kuru strādāt. Tomēr starp katru metodi ir nelielas nianses. Es atstāšu jūs, lai pārbaudītu viņu dokumentāciju.

Ceturtais izsaukuma elles risinājums: izmantojiet asinhronās funkcijas

Lai izmantotu asinhronās funkcijas, vispirms jāzina divas lietas:

  1. Kā pārvērst atzvanīšanu solījumos (lasīt iepriekš)
  2. Kā izmantot asinhronās funkcijas (izlasiet šo, ja jums nepieciešama palīdzība).

Izmantojot asinhronās funkcijas, jūs varat rakstīt tā makeBurger, it kā tas atkal būtu sinhrons!

const makeBurger = async () => { const beef = await getBeef(); const cookedBeef = await cookBeef(beef); const buns = await getBuns(); const burger = await putBeefBetweenBuns(cookedBeef, buns); return burger; }; // Make and serve burger makeBurger().then(serve);

Šeit ir viens uzlabojums makeBurger. Jūs varat droši nokļūt divus palīgus uz getBunsun getBeeftajā pašā laikā. Tas nozīmē, ka jūs varat awaitviņus abus Promise.all.

const makeBurger = async () => { const [beef, buns] = await Promise.all(getBeef, getBuns); const cookedBeef = await cookBeef(beef); const burger = await putBeefBetweenBuns(cookedBeef, buns); return burger; }; // Make and serve burger makeBurger().then(serve);

(Piezīme: Jūs varat darīt to pašu ar solījumiem ... taču sintakse nav tik jauka un skaidra kā asinhronizācijas / gaidīšanas funkcijas).

Iesaiņošana

Atzvana elle nav tik ellīga, kā jūs domājat. Ir četri vienkārši veidi, kā pārvaldīt atzvanīšanas elli:

  1. Rakstiet komentārus
  2. Sadaliet funkcijas mazākās funkcijās
  3. Izmantojot solījumus
  4. Izmantojot Async / await

Šis raksts sākotnēji tika ievietots manā emuārā.

Reģistrējieties manam biļetenam, ja vēlaties vairāk rakstu, kas palīdzēs jums kļūt par labāku frontend izstrādātāju.