Callback va Callback Hell
Muammodan boshlaylik
Foydalanuvchi profilini ko'rsatish uchun uchta ma'lumot kerak: avval foydalanuvchi, keyin uning postlari, keyin postlarning izohlari. Har bir qadam oldingi natijaga bog'liq.
Asinxron operatsiyalar bilan bu qanday yozilardi? Ketma-ket bo'lishi uchun har bir funksiyaga "tugaganda nima qil" deb aytish kerak. Bu — callback.
getUser(id, function(user) {
getPosts(user.id, function(posts) {
getComments(posts[0].id, function(comments) {
showProfile(user, posts, comments);
});
});
});
Har bir qadam oldingi qadamning ichiga kirdi. Uchta qadam — uchta daraja. O'nta qadam bo'lsa nima bo'lar edi?
Bu ko'rinish — Callback Hell.
Callback nima?
Callback — boshqa funksiyaga argument sifatida beriladigan funksiya. "Sen ish tugaganida meni chaqir" degan ma'noda.
Hayotiy o'xshatma
Do'stingizdan qarz so'radingiz. U: "Hozir naqd pul yo'q, lekin kechqurun uyga kelsang beraman" dedi. Siz kechqurun bordingiz — u pul berdi. Bu yerda "kechqurun boring" — callback. Hozir emas, ma'lum narsa bo'lgandan keyin chaqiriladi.
JavaScript da:
// "Ish tugaganda shu funksiyani chaqir" — callback berish
setTimeout(function() {
console.log("1 soniyadan keyin chaqirildi");
}, 1000);
Birinchi argument — funksiya (callback). setTimeout 1 soniyadan keyin uni chaqiradi.
Callback Hell nima va nima uchun yomon?
Callback Hell — callback ichida callback, u ichida yana callback. Har bir yangi qadam bir daraja chuqurroqqa kiradi.
login(email, parol, function(xato, token) {
if (xato) { xatoBoshqar(xato); return; }
getProfile(token, function(xato, profil) {
if (xato) { xatoBoshqar(xato); return; }
getPosts(profil.id, function(xato, postlar) {
if (xato) { xatoBoshqar(xato); return; }
getComments(postlar[0].id, function(xato, izohlar) {
if (xato) { xatoBoshqar(xato); return; }
render(profil, postlar, izohlar);
});
});
});
});
Bu kodga bir daqiqa qarang. Nima yomon?
O'qish qiyin. Har bir qadam o'ngga siljiydi. To'rtinchi darajada kod ekranning o'rtasida — 5-6 darajada ekrandan chiqib ketadi.
Xato boshqaruvi takrorlanadi. Har qadamda bir xil if (xato) { ... return; } — to'rtta marta. Agar xato boshqaruvi o'zgarsa — to'rt joyda o'zgartirish kerak.
O'rtaga qadam qo'shish qiyin. Profil va postlar orasiga yangi so'rov qo'shsangiz — hamma keyingi qatlamlar bir daraja chuqurroqqa tushadi.
O'zgaruvchi ko'rish doirasi. profil ni getComments ichida ishlatmoqchisiz? Imkoni yo'q — u tashqarida e'lon qilingan emas.
Error-first callback — Node.js kelishuvi
Node.js da asinxron funksiyalar ma'lum bir tartib bilan callback qabul qiladi: birinchi argument — xato, ikkinchi — natija.
fs.readFile("fayl.txt", "utf8", function(xato, mazmun) {
// ↑ ↑
// birinchi: xato ikkinchi: natija
if (xato) {
console.error("Fayl o'qilmadi:", xato.message);
return;
}
console.log(mazmun);
});
Nima uchun xato birinchi? Agar natija birinchi bo'lsa — dasturchi xatoni e'tiborsiz qoldirib, to'g'ridan natijani ishlatishi mumkin. Xato birinchi bo'lsa — uni tekshirmasdan o'tib ketish qiyinroq.
Callback Hell dan qochish — funksiyalarga ajratish
Callback Hell ni bartaraf etishning birinchi usuli — har bir qadamni alohida funksiyaga ajratish:
// ❌ Callback Hell
login(email, parol, function(xato, token) {
getProfile(token, function(xato, profil) {
getPosts(profil.id, function(xato, postlar) {
render(profil, postlar);
});
});
});
// ✅ Ajratilgan
function loginBoldi(xato, token) {
if (xato) return xatoBoshqar(xato);
getProfile(token, profilKeldi);
}
function profilKeldi(xato, profil) {
if (xato) return xatoBoshqar(xato);
getPosts(profil.id, function(xato, postlar) {
if (xato) return xatoBoshqar(xato);
render(profil, postlar);
});
}
login(email, parol, loginBoldi);
Yaxshiroq — lekin hali ham callback zanjiri. O'zgaruvchilarni qatlamlar orasida o'tkazish qiyin. Xatolar uchun hali ham takroriy kod.
To'liq yechim — Promise (keyingi dars).
Callback Hell ning asl muammosi
Callback Hell faqat ko'rinish muammosi emas. Chuqurroq muammo bor.
Parallel ikkita so'rov qilish kerak bo'lsa nima qilasiz?
// Foydalanuvchi va mahsulotlar — bir-biriga bog'liq emas, parallel bo'lishi kerak
let foydalanuvchi, mahsulotlar;
getUser(id, function(xato, u) {
foydalanuvchi = u;
if (foydalanuvchi && mahsulotlar) {
render(foydalanuvchi, mahsulotlar); // ← ikkalasi kelganda
}
});
getProducts(function(xato, p) {
mahsulotlar = p;
if (foydalanuvchi && mahsulotlar) {
render(foydalanuvchi, mahsulotlar); // ← bir xil kod, ikkinchi joyda
}
});
Ikki so'rov — qo'lda flag, takror kod. Promise da bu Promise.all bilan bir qatorda yechiladi.
Tez-tez uchraydigan xatolar
Callbackni ikki marta chaqirish:
Asinxron funksiya yozayotganda xato ham, natija ham bo'lsa — callback ikki marta chaqirilmasligi kerak. Bu jiddiy xato.
function asinxronIsh(callback) {
if (xatoHolat) {
callback(new Error("Xato"));
// return yo'q — keyingi qator ham ishlaydi!
}
callback(null, natija); // ← ikkinchi marta chaqirildi
}
// ✅ — return bilan
function asinxronIsh(callback) {
if (xatoHolat) {
return callback(new Error("Xato")); // ← return: to'xtadi
}
callback(null, natija);
}
Xulosa
Callback — boshqa funksiyaga argument sifatida beriladigan funksiya. Ish tugaganda chaqiriladi.
Callback Hell — callback ichida callback. O'qish qiyin, xato boshqaruvi takrorlanadi, parallel ish og'ir.
Error-first — Node.js kelishuvi: birinchi argument xato, ikkinchi natija.
Callback — asinxron dasturlashning birinchi usuli edi. Uning muammolari Promise ni tug'dirdi.
Keyingi dars
37-dars: Promise — asoslar, then, catch, finally