Három bejegyzés sorozat fog következni egymás után, melyek azt mutatják be, hogyan jutottunk el az aszinkron JavaScript világában az ún. CallBack Hell-nek nevezett jelenségtől a Promise-okon át egészen az Async await-ig.
Ez a bejegyzés, tehát a Callback-ek poklát, a Callback Hell-t tárja elénk, amivel az ES6 előtt gyakran találkozhattak a JavaScript programozók.

Gyakorlati példa – Gasztroblog
A szemléltetéshez egy példát hoztam. Legyen adott egy képzeletbeli gasztroblog, egy receptekkel teli weboldal, ahonnan recepteket fogunk lekérdezni. Feltételezzük, hogy a weboldal nagy terheltségű, ezért a késéreinkre néhány másodperces késéssel kapunk választ. Csakúgy, mint élesben :).
Minden bizonnyal kell egy funkció, egy függvény a lekérdezésekhez:
function receptLekerdez() {...}
A függvényen belül az aszinkron működést, setTimeout függvényekkel fogjuk szimulálni. Mintha egy valóban működő, nagy forgalmú weboldalról kérdeznénk le adatokat, ahol a kérésekre kapott válaszok megérkezéséig eltelik néhány másodperc. A setTimeout-okkal ugyanígy fogjuk érzékeltetni, hogy a lekérdezések néhány másodpercig tartanak.
Receptek lekérdezése
Először is létrehozunk egy „külső” setTimeout függvényt, ami mondjuk 2 másodperc, azaz 2000 milliszekundum alatt visszaad néhány recept azonosítót, amik egész számok. A példa kedvéért természetesen most minden „beégetett adat”. Tehát nem igazi kérést intézünk a weboldal felé, hanem azt feltételezzük, hogy ezek a válaszok érkeztek. Szóval a lekért azonosítókat egy tömbben tároljuk. Szóval:
function receptLekerdez() {
setTimeout(() => {
const receptID = [676, 102, 34, 1089, 321];
A konzolba pedig ki is íratjuk ennek a tömbnek a tartalmát:
console.log(receptID);
}, 2000);
}
Kipróbáláshoz meghívjuk a függvényt:
receptLekerdez();
Nézzük meg a böngészőben! A fejlesztői konzolt nyissuk meg, magát a weboldalt pedig töltsük újra. Azt látjuk, hogy 2 másodperc elteltével a konzolba íródott a recept azonosítókat tartalmazó tömb. Most azt a szitut játszottuk el, hogy a forgalmas receptes oldaltól lekérdeztünk néhány kaját. Eddig az valósult meg, hogy visszakaptunk egy tömböt, ami a lukulluszi lakomák azonosító számait tartalmazza.
Konkrét recept lekérdezése
Fejlesszük tovább teszt alkalmazásunkat úgy, hogy kérjünk le egy konkrét receptet egy azonosító (id) alapján. Ehhez azt fogjuk tenni, hogy az utolsó console.log utasítás után, beteszünk még egy setTimeout függvényt. Ő fogja visszaadni a konkrét receptet, mondjuk 1,5 másodperc múlva:
setTimeout((id) => {
}, 1500
A setTimeout függvénynek az első paraméterben átadunk egy callback-et, ami egyetlen paramétert vár. Ez lesz a recept azonosítója. Második paraméter az 1500, ami az eltelt 1,5 másodpercet jelenti. Vagyis azt szimuláljuk, hogy 1,5 másodperc alatt kapjuk vissza a weboldaltól a kiválasztott receptet. A setTimeout harmadik paraméterében adjuk meg azt a recept azonosítót, amit a callback vár, ami alapján ő lekéri a receptet:
setTimeout((id) => {
// callBack kifejtése
}, 1500, receptID[1]);
Tehát az id-be a receptID[1]-ben levő érték kerül. A példában konkrétan a 102 kerül az id helyére, mert a tömbben ez van az első indexen. A tömbök indexelése ugyebár 0-tól indul JavaScriptben.
Amit a callback-en belül csinálunk az egy recept lekérdezése és megjelenítése: a receptről az egyszerűség kedvéért csak a címet és a kategóriát adjuk vissza egy objektumban. Kiíratáshoz pedig az ES6-ban megjelent template sztringet használjuk:
setTimeout((id) => {
const recept = {
cim: ‘Gulyás leves’,
kategoria: ‘Levesek’
};
console.log(`${id}: ${recept.cim}`);
}, 1500, receptID[1]);
Ismét próbáljuk ki a böngészőben!
2 másodperc alatt lejöttek a receptek, majd újabb 1,5 másodperc alatt a gulyás leves recept. Eddig szuper.
Receptek lekérése kategória szerint
Fűszerezzük meg a gulyást, akarom mondani a kódunkat még azzal is, hogy lekérünk egy további receptet is, ami visszaad még egy (vagy több) levest.
Újabb beágyazott setTimeout jön, aminek szintén három paramétere van. Az első egy callback, ami a lekérdezést végzi, a második a lekérdezéssel eltelt másodpercek száma és a harmadik a kategória, ami alapján a callback lekérdez még egy vagy több olyan flamót, aminek az a kategóriája, hogy leves. Nézzük:
setTimeout(kategoria => {
const levesek = [
{ cim: 'Nyírségi gombócleves', kategoria: 'Levesek' },
{ cim: 'Borsóleves', kategoria: 'Levesek' },
];
console.log(levesek);
}, 1500, recept.kategoria);
Jöhet a főpróba a böngészőben!
Ha jól dolgoztunk, akkor 2 mp alatt megjöttek a recept azonosítók.
Utána 1,5 mp alatt a gulyásleves recept. Aztán további 1,5 mp-en belül lejött két másik leves is a weboldalról.
És íme a callback hell személyesen

Mindezzel egy valós működést imitáltunk. Nézzük meg a teljes scriptet:
function receptLekerdez() {
setTimeout(() => {
const receptID = [676, 102, 34, 1089, 321];
console.log(receptID);
setTimeout((id) => {
const recept = {
cim: 'Gulyás leves',
kategoria: 'Levesek'
};
console.log(`${id}: ${recept.cim}`);
setTimeout(kategoria => {
const levesek = [
{ cim: 'Nyírségi gombócleves', kategoria: 'Levesek' },
{ cim: 'Borsóleves', kategoria: 'Levesek' },
];
console.log(levesek);
}, 1500, recept.kategoria);
}, 1500, receptID[1]);
}, 2000);
}
receptLekerdez();
Látható, hogy ezekkel az egymásba ágyazott setTimeout-okkal, eléggé gázul néz ki a program, és ez az, amit callback hell-nek nevezünk. A callback hell jelenti tehát az egymásba ágyazott callback-ek sokszor áttekinthetetlen láncolatát. És a fenti példa még az enyhébb verzió… Ennél sokkal durvább esetek is voltak.
Na ezt a poklot szüntették meg az ES6 bevezetésével megjelent Promise-ok. Ezekről lesz szó a következő bejegyzésben.