Callback va Callback Hell

~15 daq · JavaScript · Asinxron JavaScript

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