Hogyan kell kezelni a beágyazott visszahívásokat és elkerülni a „visszahívási poklot”

A JavaScript furcsa nyelv. Időnként meg kell küzdenie egy visszahívással, amely egy másik visszahívásban van, ami egy másik visszahívásban van.

Az emberek ezt a mintát szeretettel hívják visszahívó pokolnak .

Valahogy így néz ki:

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

Ez a JavaScript az Ön számára. Elgondolkodtató, hogy beágyazott visszahívásokat látunk, de nem hiszem, hogy ez egy pokol. A „pokol” kezelhető, ha tudja, mit kezdjen vele.

Visszahívásokkor

Feltételezem, hogy tudod, mi a visszahívás, ha ezt a cikket olvasod. Ha nem, olvassa el ezt a cikket a visszahívások bemutatásához, mielőtt folytatja. Ott arról beszélünk, hogy mik a visszahívások, és miért használja őket a JavaScript-ben.

Megoldások a visszahívási pokolra

A visszahívási pokolnak négy megoldása van:

  1. Írj megjegyzést
  2. A funkciók felosztása kisebb funkciókra
  3. Az ígéretek felhasználása
  4. Az Async / await használatával

Mielőtt belemerülnénk a megoldásokba, építsünk össze egy visszahívási poklot. Miért? Mert túl elvont ahhoz firstFunction, hogy lássuk secondFunction, és thirdFunction. Konkrétá akarjuk tenni.

Visszahívó pokol felépítése

Képzeljük el, hogy hamburgert próbálunk készíteni. A hamburger elkészítéséhez a következő lépéseket kell végrehajtanunk:

  1. Hozzávalók beszerzése (feltételezzük, hogy marhahamburger)
  2. Főzd meg a marhahúst
  3. Szerezzen hamburger zsemlét
  4. Tegye a főtt marhahúst a zsemle közé
  5. Tálalja a hamburgert

Ha ezek a lépések szinkronok, akkor egy olyan funkciót fog megnézni, amely hasonlít ehhez:

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

Tegyük fel azonban, hogy forgatókönyvünk szerint mi magunk sem készíthetjük a hamburgert. Segítőket kell oktatnunk a hamburger elkészítésének lépésein. Miután utasíthatja a segítő, meg kell WAIT a segítő a végéig, mielőtt elkezdjük a következő lépésben.

Ha várni akarunk valamire a JavaScript-ben, akkor visszahívást kell használnunk. A hamburger elkészítéséhez előbb meg kell szereznünk a marhahúst. Csak akkor főzhetjük a marhahúst, ha megszerezzük a marhahúst.

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

A marhahús főzéséhez át kell adnunk beefa cookBeeffunkciót. Különben nincs mit főzni! Ezután meg kell várnunk, amíg a marhahús megsül.

Amint a marhahús megfőtt, zsemlét kapunk.

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

Miután megkaptuk a zsemlét, a pogácsát a zsemle közé kell tennünk. Itt alakul ki egy hamburger.

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

Végül tálalhatjuk a hamburgert! De nem térhetünk vissza burger, makeBurgermert aszinkron. El kell fogadnunk egy visszahívást a hamburger felszolgálására.

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) })

(Nagyon jól éreztem magam, amikor ezt a visszahívási példát készítettem.)

Első megoldás a visszahívási pokolra: Írjon megjegyzéseket

A makeBurgervisszahívási pokol egyszerűen érthető. Elolvashatjuk. Csak ... nem néz ki szépen.

Ha makeBurgerelőször olvasol , akkor gondolkodhatsz: „Mi a fenének van szükségünk annyi visszahívásra egy hamburger készítéséhez? Nincs értelme! ”.

Ilyen esetben megjegyzéseket szeretne hagyni a kód magyarázatához.

// 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); }); }); }); }); };

Most ahelyett, hogy a "wtf ?!" -re gondoltam volna amikor látja a visszahívási poklot, megértik, miért kell ezt így megírni.

A visszahívási pokol második megoldása: A visszahívásokat különféle funkciókra bontsa

Visszahívási pokolunk már erre is példa. Hadd mutassam meg neked a lépésenkénti kötelező kódot, és meglátod, miért.

Az getBeefelső visszahívásunkhoz a hűtőszekrényhez kell mennünk, hogy megkapjuk a marhahúst. Két hűtő van a konyhában. A megfelelő hűtőhöz kell mennünk.

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

A marhahús főzéséhez sütőbe kell helyezni a marhahúst; fordítsa a sütőt 200 fokra, és várjon húsz percet.

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

Most képzelje el, ha be kell írni ezeket a lépéseket makeBurger... valószínűleg elájul a rengeteg kódtól!

Konkrét példaként a visszahívások kisebb funkciókra bontásáról olvashatja ezt a kis részt a visszahívási cikkemben.

A visszahívási pokol harmadik megoldása: Használja az ígéreteket

Feltételezem, hogy tudod, mi az ígéret. Ha nem, kérjük, olvassa el ezt a cikket.

Az ígéretek sokkal könnyebben kezelhetik a visszahívást. A fent látható beágyazott kód helyett ez lesz:

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));

Ha ígéretekkel kihasználja az egy argumentumú stílus előnyeit, akkor a fentieket ezzel módosíthatja:

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

Sokkal könnyebben olvasható és kezelhető.

De a kérdés az, hogyan lehet a visszahívás-alapú kódot ígéret-alapú kódgá alakítani.

A visszahívások átalakítása ígéretekké

A visszahívások ígéretekké alakításához minden visszahíváshoz új ígéretet kell létrehoznunk. Mi lehet resolveaz ígéretet, amikor a visszahívás sikeres. Vagy rejectmegígérhetjük, ha a visszahívás nem sikerül.

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); }); };

A gyakorlatban valószínűleg már a visszahívásokat is írnák Önnek. A Node használata esetén minden visszahívást tartalmazó funkció szintaxisa megegyezik:

  1. A visszahívás lenne az utolsó érv
  2. A visszahívásnak mindig két argumentuma lesz. És ezek az érvek ugyanabban a sorrendben vannak. (Először hiba, majd bármi, ami érdekel).
// 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 });

Ha a visszahívásnak ugyanaz a szintaxisa, akkor olyan könyvtárakat használhat, mint az ES6 Promisify vagy a Denodeify (de-node-ify) a visszahívást ígéretté. Ha a Node v8.0 vagy újabb verziót használja, használhatja a util.promisify fájlt.

Mindhárman működnek. Bármely könyvtárat kiválaszthatja. Az egyes módszerek között azonban vannak apró árnyalatok. Hagylak, hogy megnézhesd a dokumentációjukat.

A visszahívás poklának negyedik megoldása: Használjon aszinkron függvényeket

Az aszinkron funkciók használatához először két dolgot kell tudnia:

  1. A visszahívások ígéretekké alakítása (olvasható fent)
  2. Az aszinkron funkciók használata (olvassa el ezt, ha segítségre van szüksége).

Az aszinkron funkciókkal úgy írhat makeBurger, mintha ismét szinkron lenne!

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);

There’s one improvement we can make to the makeBurger here. You can probably get two helpers to getBuns and getBeef at the same time. This means you can await them both with 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);

(Note: You can do the same with Promises… but the syntax isn’t as nice and as clear as async/await functions).

Wrapping up

Callback hell isn’t as hellish as you think. There are four easy ways to manage callback hell:

  1. Write comments
  2. Split functions into smaller functions
  3. Using Promises
  4. Using Async/await

This article was originally posted on my blog.

Sign up for my newsletter if you want more articles to help you become a better frontend developer.