17-bo‘lim

Promise va async/await

Promise nima, then va catch, async/await sintaksisi, parallel bajarish, Promise.all va xatolarni to'g'ri ushlash.

🕑 18 daqiqa o‘qish 📄 645 so‘z 👁 3 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Promise yaratish
  2. Zanjir
  3. async va await
  4. try/catch endi ishlaydi
  5. Ketma-ket va parallel
  6. Promise.all va uning qarindoshlari
  7. Ushlamagan rad etish
  8. Amaliy naqsh: vaqt chegarasi
  9. Ketma-ket qayta urinish
  10. Xulosa

Promise - "kelajakda qiymat bo'ladi" degan va'da. U callback jahannamini yechadi va try/catch ni asinxron kodda ishlatadi.

Promise yaratish #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((bajarildi) => {
        setTimeout(() => bajarildi(qiymat), ms);
    });
}

function xatoli(ms) {
    return new Promise((bajarildi, rad) => {
        setTimeout(() => rad(new Error("bajarilmadi")), ms);
    });
}

kutish(10, "natija").then((q) => console.log("then:", q));

xatoli(10).catch((x) => console.log("catch:", x.message));

console.log("Sinxron kod avval");
Natija
Sinxron kod avval
then: natija
catch: bajarilmadi
Promise ning uch holati pending kutilmoqda resolve() reject() fulfilled .then() ishlaydi rejected .catch() ishlaydi Holat bir marta o'zgaradi va keyin O'ZGARMAYDI
Bajarilgan promise ni bekor qilib bo'lmaydi

Zanjir #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((bajarildi) => setTimeout(() => bajarildi(qiymat), ms));
}

kutish(5, 10)
    .then((son) => {
        console.log("1-qadam:", son);
        return son * 2;
    })
    .then((son) => {
        console.log("2-qadam:", son);
        return kutish(5, son + 5);        // promise qaytarsak - kutiladi
    })
    .then((son) => {
        console.log("3-qadam:", son);
    })
    .finally(() => {
        console.log("finally - har doim");
    });
Natija
1-qadam: 10
2-qadam: 20
3-qadam: 25
finally - har doim
then ichida return qilishni unutmang
JavaScript
kutish(5, 1)
    .then((s) => {
        kutish(5, s + 1);          // return YO'Q!
    })
    .then((s) => console.log(s));  // undefined

return bo'lmasa, keyingi then kutmaydi va undefined oladi.

Bu callback jahannamining Promise dagi ko'rinishi - eng ko'p uchraydigan xato.

async/await da bu muammo yo'q, chunki await majburan kutadi. Shuning uchun zamonaviy kodda then zanjiri kamdan-kam ishlatiladi.

async va await #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((bajarildi) => setTimeout(() => bajarildi(qiymat), ms));
}

async function bajarish() {
    console.log("Boshlandi");

    const birinchi = await kutish(5, 10);
    console.log("1-natija:", birinchi);

    const ikkinchi = await kutish(5, birinchi * 2);
    console.log("2-natija:", ikkinchi);

    return ikkinchi + 5;
}

bajarish().then((natija) => console.log("Yakuniy:", natija));

console.log("Sinxron kod");
Natija
Boshlandi
Sinxron kod
1-natija: 10
2-natija: 20
Yakuniy: 25
async funksiya har doim Promise qaytaradi
JavaScript
async function oddiy() {
    return 42;                  // son qaytardik
}

console.log(oddiy());           // Promise { 42 } - Promise!
oddiy().then((n) => console.log(n));   // 42

Ichida await bo'lmasa ham, async funksiyaning natijasi har doim Promise ga o'raladi.

Shuning uchun uni chaqirganda ham await yoki .then() kerak:

JavaScript
const natija = await oddiy();          // 42

Teskarisi ham to'g'ri: await oddiy qiymat bilan ham ishlaydi - u shunchaki uni Promise ga o'raydi:

JavaScript
const son = await 42;           // 42

try/catch endi ishlaydi #

JavaScript
function xatoli(ms) {
    return new Promise((b, rad) => {
        setTimeout(() => rad(new Error("server javob bermadi")), ms);
    });
}

async function bajarish() {
    try {
        const natija = await xatoli(5);
        console.log("Bu chiqmaydi:", natija);
    } catch (xato) {
        console.log("Ushlandi:", xato.message);
    } finally {
        console.log("Tozalash bajarildi");
    }
}

bajarish();
Natija
Ushlandi: server javob bermadi
Tozalash bajarildi
Bu async/await ning asosiy afzalligi

16-bo'limda try/catch asinxron callback xatosini ushlamasligini ko'rdik.

await bu muammoni hal qiladi: u funksiyani "to'xtatib turadi" va xato kelganda uni sinxron kod kabi tashlaydi.

Natijada asinxron kod sinxron kabi o'qiladi:

JavaScript
try {
    const foydalanuvchi = await olish(id);
    const buyurtmalar = await buyurtmalar(foydalanuvchi.id);
    korsatish(buyurtmalar);
} catch (xato) {
    xatoKorsatish(xato);
}

Bitta try/catch butun zanjirni qamrab oladi.

Ketma-ket va parallel #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((b) => setTimeout(() => b(qiymat), ms));
}

async function ketmaKet() {
    const boshi = Date.now();

    const a = await kutish(30, "a");
    const b = await kutish(30, "b");
    const c = await kutish(30, "c");

    return { natija: [a, b, c], vaqt: Date.now() - boshi };
}

async function parallel() {
    const boshi = Date.now();

    const [a, b, c] = await Promise.all([
        kutish(30, "a"),
        kutish(30, "b"),
        kutish(30, "c")
    ]);

    return { natija: [a, b, c], vaqt: Date.now() - boshi };
}

(async () => {
    const k = await ketmaKet();
    const p = await parallel();

    console.log("Ketma-ket natija:", k.natija.join(", "));
    console.log("Parallel natija :", p.natija.join(", "));
    console.log("Ketma-ket 80 ms dan ko'pmi?:", k.vaqt > 80);
    console.log("Parallel 60 ms dan kammi? :", p.vaqt < 60);
    console.log("Parallel tezroqmi?        :", p.vaqt < k.vaqt);
})();
Natija
Ketma-ket natija: a, b, c
Parallel natija : a, b, c
Ketma-ket 80 ms dan ko'pmi?: true
Parallel 60 ms dan kammi? : true
Parallel tezroqmi?        : true
Sikl ichida await - eng ko'p uchraydigan sekinlik
JavaScript
// SEKIN - har biri navbat kutadi
const natijalar = [];
for (const id of idlar) {
    natijalar.push(await malumotOlish(id));
}

100 ta so'rov × 100 ms = 10 soniya.

JavaScript
// TEZ - hammasi bir vaqtda boshlanadi
const natijalar = await Promise.all(idlar.map((id) => malumotOlish(id)));

100 ta so'rov parallel = ~100 ms.

Qachon ketma-ket kerak:

  • Keyingi so'rov oldingisining natijasiga bog'liq bo'lsa;
  • Server so'rovlar sonini cheklagan bo'lsa;
  • Tartib muhim bo'lsa (lekin Promise.all tartibni saqlaydi!).

Promise.all natijalarni kirish tartibida qaytaradi - qaysi biri tez tugashidan qat'i nazar.

Promise.all va uning qarindoshlari #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((b) => setTimeout(() => b(qiymat), ms));
}

function xato(ms, xabar) {
    return new Promise((b, rad) => setTimeout(() => rad(new Error(xabar)), ms));
}

(async () => {
    // all - bittasi yiqilsa, hammasi yiqiladi
    try {
        await Promise.all([kutish(5, "a"), xato(5, "yiqildi"), kutish(5, "c")]);
    } catch (x) {
        console.log("all      -> xato:", x.message);
    }

    // allSettled - hammasining natijasini beradi
    const natijalar = await Promise.allSettled([
        kutish(5, "a"),
        xato(5, "yiqildi")
    ]);
    console.log("allSettled:", natijalar.map((n) => n.status).join(", "));

    // race - birinchi tugagani (xato bo'lsa ham)
    const tez = await Promise.race([kutish(5, "tez"), kutish(50, "sekin")]);
    console.log("race      ->", tez);

    // any - birinchi MUVAFFAQIYATLI
    const birinchi = await Promise.any([xato(5, "yiqildi"), kutish(20, "ishladi")]);
    console.log("any       ->", birinchi);
})();
Natija
all      -> xato: yiqildi
allSettled: fulfilled, rejected
race      -> tez
any       -> ishladi
MetodQachon tugaydiXato bo'lsa
allHammasi tugagandaDarhol yiqiladi
allSettledHammasi tugagandaHech qachon yiqilmaydi
raceBirinchi tugagandaBirinchisi xato bo'lsa - yiqiladi
anyBirinchi muvaffaqiyatHammasi yiqilsa - yiqiladi
allSettled - qismiy muvaffaqiyat uchun

Uch xil ma'lumot yuklayotgan sahifani tasavvur qiling. Bittasi yiqilsa, Promise.all bilan hammasi yo'qoladi.

allSettled esa har birining holatini beradi:

JavaScript
const natijalar = await Promise.allSettled([
    profilOlish(),
    xabarlarOlish(),
    sozlamalarOlish()
]);

for (const n of natijalar) {
    if (n.status === "fulfilled") {
        korsatish(n.value);
    } else {
        xatoKorsatish(n.reason);
    }
}

Har element ikki shakldan biri bo'ladi:

JavaScript
{ status: "fulfilled", value: ... }
{ status: "rejected",  reason: ... }

Foydalanuvchi kamida ishlagan qismini ko'radi.

Ushlamagan rad etish #

JavaScript
// Promise yaratildi, lekin catch qo'yilmadi
const xatoli = new Promise((b, rad) => {
    setTimeout(() => rad(new Error("hech kim ushlamadi")), 5);
});

// Node bunday holatni aniqlaydi
process.on("unhandledRejection", (sabab) => {
    console.log("Ushlanmagan rad etish:", sabab.message);
});

setTimeout(() => console.log("Dastur davom etdi"), 30);
Natija
Ushlanmagan rad etish: hech kim ushlamadi
Dastur davom etdi
Har bir Promise ushlanishi kerak
JavaScript
async function saqlash() {
    await serverga Yuborish();     // xato bo'lsa nima bo'ladi?
}

saqlash();                          // catch yo'q - xato yo'qoladi

Node.js da bu dasturni to'xtatadi (versiya 15 dan boshlab). Brauzerda esa konsolga chiqadi va sezilmay qolishi mumkin.

To'g'ri variantlar:

JavaScript
saqlash().catch((x) => console.error("Saqlab bo'lmadi:", x));
JavaScript
try {
    await saqlash();
} catch (x) {
    xatoKorsatish(x);
}

Global tutqich ham qo'ying - kutilmagan xatolarni jurnalga yozish uchun:

JavaScript
// Brauzerda
window.addEventListener("unhandledrejection", (h) => {
    jurnalgaYozish(h.reason);
    h.preventDefault();          // konsolga chiqmasin
});

Amaliy naqsh: vaqt chegarasi #

JavaScript
function kutish(ms, qiymat) {
    return new Promise((b) => setTimeout(() => b(qiymat), ms));
}

function chegaraBilan(promise, ms) {
    const chegara = new Promise((b, rad) => {
        setTimeout(() => rad(new Error(`${ms} ms dan oshdi`)), ms);
    });

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

(async () => {
    try {
        const tez = await chegaraBilan(kutish(10, "ulgurdi"), 50);
        console.log("Tez so'rov  :", tez);
    } catch (x) {
        console.log("Tez so'rov xato:", x.message);
    }

    try {
        await chegaraBilan(kutish(100, "kech"), 20);
        console.log("Bu chiqmaydi");
    } catch (x) {
        console.log("Sekin so'rov:", x.message);
    }
})();
Natija
Tez so'rov  : ulgurdi
Sekin so'rov: 20 ms dan oshdi

Ketma-ket qayta urinish #

JavaScript
function kutish(ms) {
    return new Promise((b) => setTimeout(b, ms));
}

let urinishlar = 0;

async function beqarorAmal() {
    urinishlar++;
    if (urinishlar < 3) {
        throw new Error(`${urinishlar}-urinish yiqildi`);
    }
    return "muvaffaqiyat";
}

async function qaytaUrinish(amal, maks = 5, kechikish = 5) {
    for (let i = 1; i <= maks; i++) {
        try {
            return await amal();
        } catch (xato) {
            console.log(`  ${xato.message}`);
            if (i === maks) {
                throw xato;
            }
            await kutish(kechikish * i);      // har safar ko'proq kutamiz
        }
    }
}

(async () => {
    const natija = await qaytaUrinish(beqarorAmal);
    console.log("Natija:", natija);
    console.log("Urinishlar soni:", urinishlar);
})();
Natija
  1-urinish yiqildi
  2-urinish yiqildi
Natija: muvaffaqiyat
Urinishlar soni: 3
Eksponensial orqaga chekinish (exponential backoff)

Yuqoridagi misolda kechikish har urinishda ortadi: 5, 10, 15 ms.

Real tizimlarda odatda ikki barobar oshiriladi:

JavaScript
await kutish(kechikish * 2 ** i);      // 100, 200, 400, 800 ms

Nima uchun: server yuklangan bo'lsa, darhol qayta urinish uni yanada ko'proq yuklaydi. Kutish vaqtini oshirish serverga tiklanish imkonini beradi.

Ko'p mijoz bir vaqtda qayta urinsa, ular birgalikda yana hujum qiladi. Buning oldini olish uchun tasodifiy qo'shimcha ("jitter") qo'shiladi:

JavaScript
const kechikish = asos * 2 ** i + Math.random() * 100;

Diqqat: hamma xatoni qayta urinmang. 404 yoki 401 qayta urinishdan tuzalmaydi - faqat tarmoq xatosi va 5xx uchun mantiqiy.

Amaliy topshiriq
  1. new Promise bilan kechikish funksiyasini yozing.
  2. then zanjirida return unutilsa nima bo'lishini ko'ring.
  3. async funksiya Promise qaytarishini isbotlang.
  4. try/catch bilan asinxron xatoni ushlang.
  5. Ketma-ket va parallel bajarish vaqtini o'lchang.
  6. Sikl ichidagi await ni Promise.all ga aylantiring.
  7. all va allSettled farqini xatoli promise bilan ko'rsating.
  8. race bilan vaqt chegarasi qo'ying.
  9. Ushlanmagan rad etishni global tutqich bilan ushlang.
  10. Eksponensial kechikishli qayta urinish yozing.

Xulosa #

  • Promise uch holatda: pending → fulfilled yoki rejected.
  • Holat bir marta o'zgaradi va qaytmaydi.
  • then ichida return qilishni unutmang.
  • async funksiya har doim Promise qaytaradi.
  • await bilan try/catch asinxron kodda ishlaydi.
  • Sikl ichida await - eng ko'p uchraydigan sekinlik sababi.
  • Promise.all parallel bajaradi va tartibni saqlaydi.
  • all bittasi yiqilsa yiqiladi, allSettled hech qachon.
  • race birinchi tugaganini, any birinchi muvaffaqiyatni.
  • Har bir Promise ushlanishi kerak - unhandledRejection.
  • Qayta urinishda kechikishni oshiring (backoff + jitter).

Keyingi bo'limda fetch orqali server bilan gaplashamiz.

Xatolik topdingizmi?

Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.