Promise kombinatorlari — all, allSettled, race, any

~20 daq · JavaScript · Asinxron JavaScript

Muammodan boshlaylik

Sahifani ochganda uchta alohida so'rov ketadi: foydalanuvchi ma'lumoti, uning postlari, bildirishnomalar. Ular bir-biriga bog'liq emas — birining natijasi ikkinchisiga kerak emas.

Siz .then() zanjiri bilan yozdingiz:

getUser()
  .then(() => getPosts())
  .then(() => getNotifs())
  .then(() => render());

Ishlaydi. Lekin muammo bor. Har bir so'rov oldingi tugamasdan boshlanmaydi. getUser 300ms, getPosts 200ms, getNotifs 400ms — jami 900ms kutish.

Holbuki ularni bir vaqtda yuborsa bo'ladi. Jami kutish — eng uzuni kadar: 400ms.

Bir vaqtda bir nechta Promise boshqarish uchun — kombinatorlar.


Promise.all — hammasi muvaffaqiyatli bo'lsin

Promise.all — berilgan barcha Promise larni bir vaqtda boshlaydi. Hammasi tugagach, natijalarni massiv sifatida beradi.

Hayotiy o'xshatma

Oshxonada to'y taomi tayyorlanmoqda. Bir vaqtda: bir oshpaz osh qiladi, biri go'sht qiladi, biri salat qiladi. Hamma tayyor bo'lganda — dasturxon yoziladi. Bittasi kech qolsa — dasturxon kutadi. Biri umuman qila olmasa — to'y bekor bo'ladi.

Promise.all([
  getUser(),    // 300ms
  getPosts(),   // 200ms
  getNotifs(),  // 400ms
]).then(function([foydalanuvchi, postlar, bildirishnomalar]) {
  // ← uchala tayyor bo'lgandan keyin — 400ms (eng uzuni)
  render(foydalanuvchi, postlar, bildirishnomalar);
}).catch(function(xato) {
  // ← istalgan biri xato bersa — darhol bu yerga
  xatoBoshqar(xato);
});

Natijalar massivda — kirish tartibida. getUser tez tugasa ham, uning natijasi massivda birinchi o'rinda.

Qachon ishlatiladi?

Barcha natijalar kerak va bittasi muvaffaqiyatsiz bo'lsa — hammasi bekor bo'lishi kerak bo'lganda.


Promise.allSettled — hammasi tugasin, xato bo'lsa ham

Promise.all da bittasi xato bersa — qolganlarning natijasiga yetib bo'lmaydi. Ba'zan bu to'g'ri emas. Masalan: 10 ta xabar yuborildi, 2 tasi yetib bormadi — 8 tasining natijasini bilish kerak.

Promise.allSettled — barcha Promise lar tugaguncha kutadi. Xato bo'lsa ham to'xtatmaydi. Har birining holati qaytariladi.

Hayotiy o'xshatma

Pochta tarqatish. Pochtalyon 10 ta xat tarqatib chiqdi. Hammasi yetib bormasligi mumkin — kimsasiz uy, adres noto'g'ri. Lekin pochtalyon hamma xatni ko'rib chiqadi va hisobot tuzadi: "8 tasi yetdi, 2 tasi yetmadi". Bittasi yetmaganida qolganlarni tashlab ketmaydi.

Promise.allSettled([
  fetch("/api/xabar/1"),
  fetch("/api/xabar/2"),
  fetch("/api/xabar/3"),
]).then(function(natijalar) {
  natijalar.forEach(function(natija, i) {
    if (natija.status === "fulfilled") {
      console.log(`${i+1}-xabar yuborildi ✅`);
    } else {
      console.log(`${i+1}-xabar yuborilmadi ❌:`, natija.reason.message);
    }
  });
});

Har bir natijada status ("fulfilled" yoki "rejected") va qiymat yoki sabab bor.

Qachon ishlatiladi?

Natijalar mustaqil — bittasi xato bo'lsa ham qolganlarini bilish kerak bo'lganda.


Promise.race — birinchi tugagani g'alaba

Promise.race — berilgan Promise lardan birinchi tuganining natijasini qaytaradi. Muvaffaqiyatli ham, xato ham bo'lishi mumkin.

Hayotiy o'xshatma

Poygada birinchi finishga kirgan g'alaba qozonadi. Boshqalar hali yugurayotgan bo'lsa ham — birinchi yetib kelgan o'yinchi aniqlandi, poyga tugadi.

Promise.race ning eng ko'p ishlatiladigan holati — timeout: agar server ma'lum vaqt ichida javob bermasa, xato ko'rsatish.

function vaqtCheklangan(promise, ms) {
  const timeout = new Promise(function(_, reject) {
    setTimeout(function() {
      reject(new Error(`${ms}ms dan oshdi`));
    }, ms);
  });

  return Promise.race([promise, timeout]);
}

vaqtCheklangan(fetch("/api/sekin"), 3000)
  .then(res  => res.json())
  .then(data => ko_rsat(data))
  .catch(err => console.error(err.message));
// Agar 3 soniyada javob kelmasa: "3000ms dan oshdi"

Bu yerda ikkita Promise raqobatlashdi: haqiqiy so'rov va 3 soniyalik timeout. Qaysi biri birinchi tugasa — o'sha g'alaba qozonadi.

Qachon ishlatiladi?

Timeout qo'shish kerak bo'lganda yoki bir nechta manbadan eng tezini olish kerak bo'lganda.


Promise.any — birinchi muvaffaqiyatli

Promise.any — birinchi muvaffaqiyatli Promise qaytaradi. Xato bo'lgan Promise larga e'tibor bermaydi. Hammasi xato bo'lsagina — AggregateError qaytaradi.

Hayotiy o'xshatma

Uch xil taxi ilovasi bilan bir vaqtda mashina chaqirdingiz. Qaysi biri birinchi qabul qilsa — o'sha keladi. Boshqa ikkitasi rad etsa ham — bittasi qabul qilsa yetarli.

Promise.any([
  fetch("https://server1.example.com/api"),
  fetch("https://server2.example.com/api"),
  fetch("https://server3.example.com/api"),
])
  .then(res  => res.json())
  .then(data => ko_rsat(data))
  .catch(err => console.error("Hamma server ishlamayapti"));

Agar server1 ishlamasa, server2 va server3 dan javob kutiladi. Birinchi ishlagan javob ishlatiladi.

race va any farqi

race — birinchi tugagani, xato bo'lsa ham. Agar birinchi tugagani xato bersa — catch ga tushadi.

any — birinchi muvaffaqiyatli. Xato berganlar e'tiborsiz qoladi, keyingilar kutiladi.


To'rttasini bir joyda taqqoslash

Kombinator Qachon tugaydi Xatoda
Promise.all hammasi fulfilled bo'lganda birinchi xatoda darhol rejected
Promise.allSettled hammasi tugaganda hech qachon rejected — holat beriladi
Promise.race birinchi tugaganda birinchi xato ham rejected qiladi
Promise.any birinchi fulfilled bo'lganda hammasi xato bo'lsagina rejected

Tez-tez uchraydigan xatolar

Promise.all da bittasi xato — boshqalarning natijasiga yeta olmaslik:

Ba'zan bir so'rov xato bersa ham, qolganlarini ko'rsatmoqchi bo'lasiz. Promise.all bunga imkon bermaydi — bittasi xato bersa hammasi yo'qoladi.

Yechim — har birining xatosini alohida ushlash:

Promise.all([
  getUser().catch(() => null),    // xato bo'lsa null
  getPosts().catch(() => []),     // xato bo'lsa bo'sh massiv
  getNotifs().catch(() => []),
]).then(function([foydalanuvchi, postlar, bildirishnomalar]) {
  if (!foydalanuvchi) showError("Foydalanuvchi yuklanmadi");
  render(foydalanuvchi, postlar, bildirishnomalar);
});

Massiv o'rniga alohida argument berish:

// ❌ — Promise.all massiv kutadi
Promise.all(getUser(), getPosts()); // Xato: ikkinchi argument e'tiborsiz

// ✅
Promise.all([getUser(), getPosts()]);

Xulosa

To'rtta kombinator — to'rtta turli vaziyat uchun:

Promise.all — hammasi muvaffaqiyatli bo'lishi kerak. Bittasi xato — hammasi bekor.

Promise.allSettled — hammasi tugasin. Xato bo'lsa ham holati beriladi.

Promise.race — birinchi tugagani. Timeout qo'shish uchun.

Promise.any — birinchi muvaffaqiyatli. Zaxira serverlar uchun.


Keyingi dars

39-dars: Async/Await — asoslar