Promise, async/await

Promise API

Promise klassida bir nechta foydali statik metod bor. Ular bir nechta promise'ni birga boshqarishga yordam beradi. Ushbu darsda ularning har birini misol bilan ko'rib chiqamiz.

Promise.all

Ko'pincha bir nechta promise'ni parallel ishga tushirib, hammasi tugashini kutish kerak bo'ladi. Masalan, bir nechta URL'dan ma'lumot yuklab, hammasi kelgach ishlov berish. Aynan shu uchun Promise.all mavjud.

Promise.all promise'lar (yoki iteratsiyalanadigan obyekt, odatda massiv) qabul qiladi va yangi promise qaytaradi. Bu yangi promise barcha berilgan promise'lar bajarilganda (fulfilled) natijalar massivi bilan tugaydi.

Promise.all([
  new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
  new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
  new Promise(resolve => setTimeout(() => resolve(3), 1000))  // 3
]).then(alertResult => console.log(alertResult)); // [1, 2, 3]
Natijalar massivining tartibi berilgan promise'lar tartibiga mos keladi. Ya'ni birinchi promise eng sekin tugagan bo'lsa ham, uning natijasi massivning birinchi elementi bo'ladi. Natija tayyor bo'lishini kutish tartibi muhim emas.

Ko'p qo'llaniladigan hiyla โ€” massivdagi ma'lumotlarni promise'lar massiviga aylantirib, keyin ularni Promise.all'ga o'rash:

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://api.github.com/users/jeresig'
];

// har bir url uchun fetch promise'ini yaratamiz
let requests = urls.map(url => fetch(url));

// Promise.all barcha so'rovlar tugashini kutadi
Promise.all(requests)
  .then(responses => {
    for (let response of responses) {
      console.log(response.status); // har bir javob statusi
    }
  });
Agar istalgan bitta promise reject bo'lsa (xatoga uchrasa), Promise.all darhol o'sha xato bilan reject bo'ladi. Qolgan promise'lar natijasi mutlaqo e'tiborga olinmaydi โ€” ular bekor qilinmaydi, lekin natijasi tashlab yuboriladi.
Promise.all([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('Xatolik!')), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(console.log); // Error: Xatolik!

Yuqoridagi misolda ikkinchi promise 2 soniyadan keyin reject bo'ladi. Shu zahoti Promise.all ham reject bo'ladi va catch ishga tushadi. Uchinchi promise (3 soniya) hali tugamagan bo'lsa ham, uning natijasi hech qayerda ko'rinmaydi.

Promise.allSettled

Promise.all birinchi xatoda to'xtaydi. Ba'zan esa bizga har bir promise natijasi kerak โ€” qaysi biri muvaffaqiyatli, qaysi biri xato bo'lganidan qat'i nazar. Aynan shunda Promise.allSettled ishlatiladi.

Promise.allSettled barcha promise'lar settled (yakunlangan โ€” fulfilled yoki rejected) bo'lishini kutadi va hech qachon reject bo'lmaydi. U natijalar massivini qaytaradi, unda har bir element quyidagicha:

let urls = [
  'https://api.github.com/users/iliakan',
  'https://api.github.com/users/remy',
  'https://no-such-url'
];

Promise.allSettled(urls.map(url => fetch(url)))
  .then(results => {
    results.forEach((result, num) => {
      if (result.status == 'fulfilled') {
        console.log('Javob ' + urls[num] + ': ' + result.value.status);
      }
      if (result.status == 'rejected') {
        console.log('Xato ' + urls[num] + ': ' + result.reason);
      }
    });
  });

Yuqorida results quyidagicha bo'ladi (soddalashtirilgan):

[
  {status: 'fulfilled', value: ...birinchi javob...},
  {status: 'fulfilled', value: ...ikkinchi javob...},
  {status: 'rejected', reason: ...xato obyekti...}
]
Ya'ni har bir promise uchun status va uning qiymati (yoki xato sababi) mavjud. Bu bir nechta so'rovdan bir qismi muvaffaqiyatsiz bo'lsa ham, qolganlarini qayta ishlashga imkon beradi.

Promise.race

Promise.race Promise.allga o'xshaydi, lekin faqat birinchi yakunlangan promise'ni kutadi. "Race" โ€” poyga degani: qaysi promise birinchi tugasa (fulfilled yoki rejected โ€” farqi yo'q), uning natijasi/xatosi butun natija bo'ladi.

Promise.race([
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('Xatolik!')), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(console.log); // 1

Bu yerda birinchi promise eng tez (1 soniya) tugadi, shuning uchun natija 1 bo'ldi. Undan keyingi promise'lar natijasi butunlay e'tiborga olinmaydi.

Promise.race birinchi yakunlangan promise reject bo'lsa, o'zi ham reject bo'ladi. Ya'ni g'olib promise xato bilan tugasa, natija ham xato bo'ladi.

Promise.any

Promise.any Promise.racega o'xshaydi, lekin faqat birinchi muvaffaqiyatli (fulfilled) promise'ni kutadi. Ya'ni u xatolarni "tashlab", birinchi muvaffaqiyatli natijani qaytaradi.

Promise.any([
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('Xatolik!')), 1000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).then(console.log); // 1

Bu yerda birinchi promise (1 soniya) reject bo'ldi, lekin Promise.any uni e'tiborga olmaydi. Ikkinchi promise (2 soniya) โ€” birinchi muvaffaqiyatli natija bo'lgani uchun natija 1 bo'ldi.

Agar barcha promise'lar reject bo'lsa, Promise.any maxsus AggregateError xatosi bilan reject bo'ladi. Uning errors maydonida barcha xatolar saqlanadi:

Promise.any([
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('Xato 1')), 1000)),
  new Promise((resolve, reject) => setTimeout(() => reject(new Error('Xato 2')), 2000))
]).catch(error => {
  console.log(error.constructor.name); // AggregateError
  console.log(error.errors[0]); // Error: Xato 1
  console.log(error.errors[1]); // Error: Xato 2
});

Promise.resolve va Promise.reject

Zamonaviy kodda bu ikki metod kamdan-kam kerak bo'ladi, chunki async/await ularning o'rnini bosadi. Ammo ularni bilib qo'yish foydali.

Promise.resolve(value) โ€” natijasi value bo'lgan bajarilgan (resolved) promise yaratadi:

let promise = Promise.resolve(value);
// bu quyidagi bilan bir xil:
let promise2 = new Promise(resolve => resolve(value));

U funksiya promise qaytarishi kerak bo'lganda, lekin qiymat allaqachon tayyor bo'lganda ishlatiladi. Masalan, keshdan qiymat qaytarishda:

let cache = new Map();

function loadCached(url) {
  if (cache.has(url)) {
    // keshdagi qiymatni promise sifatida qaytaramiz
    return Promise.resolve(cache.get(url));
  }
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      cache.set(url, text);
      return text;
    });
}

Promise.reject(error) โ€” natijasi error bo'lgan reject qilingan promise yaratadi. U amalda deyarli ishlatilmaydi.

Xulosa

Promise klassining 6 ta statik metodi: