WisarWisar
Dasturlash kitobi/2-QISM — JavaScript13 daqiqa

2.11-bob: Asinxron JS — callback, Promise, async/await, fetch

2-QISM — JavaScript (0 dan chuqurgacha) · 11-mavzu


1. Kirish va motivatsiya

Bu — JavaScript'ning eng muhim va eng ko'p so'raladigan mavzularidan biri. Hozirgacha kod bir zumda ishladi. Lekin real dunyoda ko'p ish vaqt oladi: tarmoqdan ma'lumot olish 0.4-bob, fayl o'qish 0.2-bob, ma'lumotlar bazasiga so'rov (6), taymer. Bularni kutib turish kerak — lekin JS bir thread'li (0.1, 0.5: bitta oshpaz)! Agar kutsa, butun sahifa qotib qoladi.

Asinxron JS aynan shu muammoni hal qiladi: ish "fonda" bajarilsin, JS esa boshqa ishni davom ettirsin, ish tugaganda natija kelsin. 0.5-bobda ko'rgan event loop — buning mexanizmi.

O'xshatish: restoran ofitsianti (JS thread). U buyurtmani oshxonaga beradi (asinxron ish), lekin oshxona pishirguncha turib kutmaydi — boshqa stollarga xizmat qiladi. Taom tayyor bo'lganda (natija), uni olib keladi. Agar ofitsiant har taom oldida turib kutsa — restoran qotib qolardi. Asinxron JS — aynan shu.

Promise, async/await, fetchsiz zamonaviy JS yo'q. Har bir API so'rovi (5, 11.5, 12), DB amali (6), fayl ishi 5.3-bob — asinxron. Bu bobni mukammal bilish — majburiy.


2. Nazariya — chuqur tushuntirish

2.1. Sinxron vs asinxron

  • Sinxron (synchronous): kod qatorma-qator, har biri oldingisini kutadi (bloklaydi).
  • Asinxron (asynchronous): uzoq ish "fonga" topshiriladi; JS kutmasdan davom etadi; natija keyin keladi.
js
console.log("1");
setTimeout(() => console.log("2"), 1000);   // 1 sek keyin (asinxron)
console.log("3");
// Natija: 1, 3, 2   "2" kutmasdan "3" chiqdi (event loop — 0.5)

Bu — 0.5-bobdagi event loop'ning amaliy ko'rinishi: setTimeout brauzer/Node'ga topshiriladi, JS davom etadi, vaqt tugagach callback navbatga (queue) qo'yiladi.

2.2. Callback va "callback hell"

Eng eski asinxron usul — callback: "ish tugagach, shu funksiyani chaqir":

js
// Callback — natija tayyor bo'lganda chaqiriladigan funksiya (2.3)
setTimeout(() => {
  console.log("1 sekund o'tdi");
}, 1000);

Lekin ketma-ket asinxron ishlar "callback hell" (do'zaxi) ga olib keladi — chuqur, o'qishsiz ichma-ich:

js
//  CALLBACK HELL — "qiyshiq piramida"
userOl(id, (user) => {
  buyurtmaOl(user, (buyurtma) => {
    tafsilotOl(buyurtma, (tafsilot) => {
      tolovOl(tafsilot, (tolov) => {
        console.log(tolov);   // 4 daraja ichga — o'qishsiz, xatoni ushlash qiyin
      });
    });
  });
});

Bu muammoni Promise hal qildi.

2.3. Promise — asinxron natija "va'dasi"

Promise — kelajakda tayyor bo'ladigan natija "va'dasi". U uch holatda bo'ladi:

text
   pending (kutilmoqda)
      │
      ├──▶ fulfilled (bajarildi) — natija bor (.then)
      └──▶ rejected (rad etildi) — xato bor (.catch)
js
const promise = new Promise((resolve, reject) => {
  // resolve — muvaffaqiyat; reject — xato
  setTimeout(() => {
    const ok = true;
    if (ok) resolve("Natija!");   // fulfilled
    else reject(new Error("Xato")); // rejected
  }, 1000);
});

promise
  .then(natija => console.log(natija))   // fulfilled bo'lsa (2.3: callback)
  .catch(xato => console.error(xato))    // rejected bo'lsa
  .finally(() => console.log("Tugadi")); // har holda

2.4. .then zanjiri — callback hell yechimi

Promise'lar zanjirlanadi (har .then yangi Promise qaytaradi) — "piramida" tekislanadi:

js
//  .then zanjiri (2.2-dagi callback hell o'rniga)
userOl(id)
  .then(user => buyurtmaOl(user))      // har qadam keyingisini qaytaradi
  .then(buyurtma => tafsilotOl(buyurtma))
  .then(tafsilot => console.log(tafsilot))
  .catch(xato => console.error(xato)); // BITTA catch — barcha xatolarga

Bu — callback hell'dan ancha o'qishli. Lekin async/await yana ham yaxshi.

2.5. async/await — eng zamonaviy, eng o'qishli

async/await — Promise'ni sinxron kabi yozish (lekin asinxron ishlaydi). Eng yaxshi usul:

  • async funksiya — doim Promise qaytaradi.
  • await — Promise tayyor bo'lishini kutadi (faqat async ichida), natijani qaytaradi.
js
//  async/await — eng o'qishli (2.4-dagi zanjir o'rniga)
async function malumotOl(id) {
  try {
    const user = await userOl(id);            // kutadi, natija oladi
    const buyurtma = await buyurtmaOl(user);  // ketma-ket, lekin tekis
    const tafsilot = await tafsilotOl(buyurtma);
    return tafsilot;
  } catch (xato) {                            // xatoni try/catch bilan (2.12)
    console.error(xato);
  }
}

await qanday ishlaydi 0.5-bob: await funksiyani pauza qiladi va davomini microtask sifatida rejalashtiradi; thread bloklanmaydi — boshqa kod ishlayveradi. Funksiya async bo'lishi shart.

2.6. async funksiya — doim Promise

js
async function f() { return 42; }
f();              // Promise<42> (son emas, Promise!)
f().then(v => console.log(v));   // 42
// yoki: const v = await f();  (boshqa async ichida)

// throw — rejected Promise (2.12)
async function g() { throw new Error("xato"); }
g().catch(e => console.log(e.message));   // "xato"

2.7. fetch — tarmoq so'rovi (0.4 amalda)

fetch — HTTP so'rov yuborish 0.4-bob; u Promise qaytaradi:

js
async function userOl(id) {
  const res = await fetch(`https://api.example.com/users/${id}`);  // 0.4
  if (!res.ok) {                          // status tekshiruvi (0.4: fetch 404'da ham "ok" emas)
    throw new Error(`HTTP ${res.status}`);
  }
  const data = await res.json();          // body'ni JSON'ga 2.8-bob — bu ham Promise!
  return data;
}

Ikki await: fetch javobi (response) keladi, keyin .json() body'ni o'qiydi — ikkalasi ham asinxron. Va res.okni tekshirish shart (0.4: fetch 404/500'da rad etmaydi).

2.8. Promise kombinatorlari — parallel ishlar

Bir nechta asinxron ishni parallel bajarish (ketma-ket emas — tezroq):

js
// Promise.all — HAMMASI tayyor bo'lsin (biri xato bo'lsa — butun rad — MDN)
const [user, postlar, izohlar] = await Promise.all([
  userOl(id), postlarOl(id), izohlarOl(id),
]);   // uchchalasi PARALLEL — eng tez

// Promise.allSettled — hammasini kut, har birining natijasini ber (xato bo'lsa ham — MDN)
const natijalar = await Promise.allSettled([a(), b(), c()]);
// [{status:"fulfilled", value}, {status:"rejected", reason}, ...]

// Promise.race — BIRINCHi tugagani — masalan timeout
const natija = await Promise.race([sorov(), timeout(5000)]);

// Promise.any — birinchi MUVAFFAQIYATLI
Kombinator Qachon tugaydi Xato bo'lsa
Promise.all hammasi fulfilled birinchi xato — butun rad
Promise.allSettled hammasi settled har biri natija (rad ham)
Promise.race birinchi settled (ok yoki xato) birinchi xato bo'lsa rad
Promise.any birinchi fulfilled hammasi rad bo'lsagina rad

Muhim: ketma-ket await (biri-birini kutadi) — sekin; mustaqil ishlar bo'lsa Promise.all bilan parallel — ancha tez.

2.9. Event loop va microtask (0.5 chuqurroq)

Promise callback'lari (.then/await) — microtask navbatiga tushadi; setTimeoutmacrotaskga. Microtasklar har doim oldin ishlaydi:

js
console.log("1");                          // sinxron
setTimeout(() => console.log("2"), 0);     // macrotask
Promise.resolve().then(() => console.log("3"));  // microtask
console.log("4");                          // sinxron
// Natija: 1, 4, 3, 2
// sinxron (1,4)  microtask (3)  macrotask (2)

(Bu — 5.1-bobda Node event loop'da yana chuqurroq ochiladi.)

2.10. Top-level await — asyncsiz await

Odatda await faqat async funksiya ichida ishlaydi 2.5-bob. Lekin ES modul (type: "module" yoki .mjs) eng yuqori darajada (funksiyasiz) ham await ishlatishga ruxsat beradi — bu top-level await:

js
//  ES modul ichida — eng yuqori darajada, async funksiyasiz
const res = await fetch("https://api.example.com/data");   // 0.4
const data = await res.json();
console.log(data);

//  Oddiy <script> yoki CommonJS'da — SyntaxError (modul emas)

Diqqat: top-level await modul yuklanishini to'xtatib turadi — bu modulga bog'liq boshqa modullar ham kutadi. Shuning uchun og'ir/sekin ishlarda ehtiyot bo'ling. Sozlash yoki bir martalik dastlabki ma'lumotni olishda qulay; lekin oddiy funksiya ichida baribir async kerak 2.5-bob.


3. Sintaksis — tez ma'lumotnoma

js
// Promise
new Promise((resolve, reject) => {});
p.then(v => {}).catch(e => {}).finally(() => {});

// async/await
async function f() {
  try {
    const x = await promise;
    return x;
  } catch (e) {}
}

// fetch (0.4)
const res = await fetch(url);
if (!res.ok) throw new Error(res.status);
const data = await res.json();

// parallel
await Promise.all([a(), b()]);        // hammasi
await Promise.allSettled([a(), b()]); // har biri natija
await Promise.race([a(), timeout()]); // birinchi

4. Batafsil kod namunalari

Misol 1 — Promise yaratish va ishlatish (2.3)

js
function kechikish(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));   // ms dan keyin resolve
}

// .then bilan
kechikish(1000).then(() => console.log("1 sek o'tdi"));

// async/await bilan 2.5-bob — o'qishliroq
async function ishla() {
  console.log("Boshlandi");
  await kechikish(1000);          // 1 sek kutadi (bloklamasdan)
  console.log("1 sek o'tdi");
}

Misol 2 — fetch va xatoni boshqarish (2.7, 2.12)

js
async function userOl(id) {
  try {
    const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
    if (!res.ok) throw new Error(`HTTP ${res.status}`);   // 0.4 — tekshir!
    const user = await res.json();                         // 2.8
    return user;
  } catch (xato) {
    console.error("Xato:", xato.message);   // tarmoq yoki HTTP xatosi (2.12)
    return null;                            // yoki qayta throw
  }
}

const user = await userOl(1);
console.log(user?.name);   // ?. — null bo'lsa qulamaydi (2.1)

Misol 3 — Ketma-ket vs parallel (2.8)

js
//  KETMA-KET — sekin (har biri oldingini kutadi: 3 sek)
async function sekin() {
  const a = await sorov1();   // 1 sek
  const b = await sorov2();   // 1 sek
  const c = await sorov3();   // 1 sek
  return [a, b, c];           // jami ~3 sek
}

//  PARALLEL — tez (uchchalasi birga: ~1 sek)
async function tez() {
  const [a, b, c] = await Promise.all([sorov1(), sorov2(), sorov3()]);
  return [a, b, c];           // jami ~1 sek (mustaqil bo'lsa)
}

Misol 4 — Timeout bilan race va allSettled (2.8)

js
// So'rovga vaqt chegarasi (race — 2.8)
function timeout(ms) {
  return new Promise((_, reject) =>
    setTimeout(() => reject(new Error("Vaqt tugadi")), ms)
  );
}
async function vaqtBilan(url) {
  return Promise.race([fetch(url), timeout(5000)]);   // 5 sek dan oshsa — xato
}

// Bir nechta so'rov — ba'zisi xato bo'lsa ham davom (allSettled — 2.8)
async function hammasiniOl(idlar) {
  const natijalar = await Promise.allSettled(idlar.map(id => userOl(id)));
  const muvaffaq = natijalar
    .filter(r => r.status === "fulfilled")   // faqat muvaffaqiyatli (2.7)
    .map(r => r.value);
  return muvaffaq;
}

5. To'g'ri va noto'g'ri holatlar

1) awaitni asyncsiz ishlatish

js
//  SyntaxError — await faqat async ichida (2.5)
function f() { const x = await p(); }

// 
async function f() { const x = await p(); }

2) awaitni unutish

js
//  Promise obyektini oladi (natija emas!)
async function f() { const data = userOl(1); console.log(data.name); }
// data — Promise, data.name — undefined

//  await bilan
const data = await userOl(1);

3) Ketma-ket await (mustaqil ishlar uchun)

js
//  mustaqil so'rovlar ketma-ket — sekin (2.8)
const a = await x(); const b = await y();

//  parallel
const [a, b] = await Promise.all([x(), y()]);

4) fetchda res.okni tekshirmaslik

js
//  404/500'da ham "muvaffaqiyat" deb davom etadi (0.4)
const res = await fetch(url);
const data = await res.json();   // xato javobni "ma'lumot" deb oladi

//  tekshir
if (!res.ok) throw new Error(res.status);

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — await is only valid in async functions

Sababi: await asyncsiz funksiyada 2.5-bob. Yechimi: funksiyani async qiling (yoki top-level await — ES modulda).

Xato 2 — Cannot read properties of undefined (Promise natijasida)

Sababi: await unutildi — Promise obyektiga murojaat (5-bo'lim). Yechimi: await qo'shing; ?. bilan himoyalaning 2.1-bob.

Xato 3 — UnhandledPromiseRejection

text
UnhandledPromiseRejection: ...

Sababi: Promise rad etildi, lekin .catch/try-catch yo'q 2.3-bob. Yechimi: har asinxron ishni try/catch (async) yoki .catch bilan o'rang 2.12-bob.

Xato 4 — fetch tarmoq xatosi vs HTTP xatosi

Sababi: fetch faqat tarmoq uzilsa rad etadi; 404/500 — rad etmaydi 0.4-bob. Yechimi: res.okni alohida tekshiring 2.7-bob.

Xato 5 — Tsiklda await (sekinlik)

js
//  har iteratsiya kutadi — sekin
for (const id of idlar) { await userOl(id); }

//  parallel
await Promise.all(idlar.map(id => userOl(id)));

Sababi: ketma-ket kutish 2.8-bob. Yechimi: Promise.all + map 2.7-bob.


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Event loop (0.5, 5.1): asinxronlikning mexanizmi; microtask/macrotask.
  • fetch/Axios (2.17, 2.18): API so'rovlari — Promise.
  • Error handling 2.12-bob: try/catch async bilan.
  • React 11.5-bob: useEffectda ma'lumot olish (async); 12 — TanStack Query/RTK Query (asinxron holatni boshqaradi).
  • Node/Express (5): so'rovlarni asinxron qayta ishlash; DB (6) — async.
  • DB (6): har query — Promise (await db.query(...)).
  • Streams 5.4-bob: katta ma'lumotni asinxron oqimda.

8. Eng yaxshi amaliyotlar (best practices)

  • async/await ishlating (.then zanjiri o'rniga) — o'qishliroq 2.5-bob.
  • Har asinxron ishni try/catch bilan o'rang 2.12-bob; fetchda res.ok tekshiring 2.7-bob.
  • Mustaqil ishlar — Promise.all (parallel, tez); tsiklda await o'rniga Promise.all + map 2.8-bob.
  • awaitni unutmang — natija Promise emas, qiymat bo'lsin (5-bo'lim).
  • Promise.allSettled — ba'zisi xato bo'lsa ham davom etish kerak bo'lganda 2.8-bob.
  • Timeout uchun Promise.race (2.8, Misol 4).
  • UnhandledPromiseRejectiondan qoching — har Promise'ning .catch/try-catch'i bo'lsin.
  • Asinxron funksiya doim Promise qaytaradi — buni esda tuting 2.6-bob.

9. Amaliy loyiha: "Ob-havo va Ma'lumot Yig'uvchi (Async)"

Asinxron JS'ni real API bilan amalda mustahkamlovchi loyiha.

Maqsad

fetch, async/await, try/catch va Promise kombinatorlarini ishlatib, bir nechta manbadan ma'lumot oluvchi, xatoga chidamli dastur yozish.

Talablar (requirements)

  1. malumotOl(url): fetch + res.ok tekshiruvi + res.json() + try/catch (Misol 2). Ochiq API ishlating (masalan jsonplaceholder.typicode.com).
  2. Ketma-ket zanjir: foydalanuvchi uning postlari birinchi postning izohlari (async/await, har biri oldingiga bog'liq).
  3. Parallel (Promise.all): bir nechta foydalanuvchini bir vaqtda oling; ketma-ket va parallel vaqtini solishtiring (console.time — 0.5) (Misol 3).
  4. Chidamli (Promise.allSettled): bir nechta so'rovdan ba'zisi xato (noto'g'ri URL) bo'lsa ham, muvaffaqiyatlilarini qaytaring (Misol 4).
  5. Timeout (Promise.race): so'rovga 5 sekund chegara; oshsa — "Vaqt tugadi" (Misol 4).
  6. Xatoni to'g'ri boshqarish: tarmoq xatosi, HTTP xatosi (404) va timeout — uch holatni alohida xabar bilan (2.12, 0.4).
  7. Retry (qayta urinish): xato bo'lsa, so'rovni 2 marta qayta urinib ko'rish (bonus).
  8. Event loop tushunchasini ko'rsatish: console.log + setTimeout + Promise tartibini bashorat qilib, tekshiring 2.9-bob.

Maslahatlar (hint)

  • Ochiq API: https://jsonplaceholder.typicode.com/users/1, /posts?userId=1, /comments?postId=1.
  • Parallel: await Promise.all([malumotOl(u1), malumotOl(u2)]).
  • Vaqt o'lchash: console.time("nom") ... console.timeEnd("nom") 0.5-bob.
  • Timeout: Promise.race([fetch(url), timeout(5000)]) (Misol 4).
  • Retry: tsikl + try/catch, muvaffaqiyatda return, xatoda davom 2.12-bob.
  • Xato turlari: !res.ok HTTP; catch tarmoq/timeout.

"Tayyor" mezonlari (acceptance criteria)

  • fetch + res.ok + json + try/catch to'g'ri ishlaydi.
  • Ketma-ket zanjir (bog'liq so'rovlar) ishlaydi.
  • Parallel (Promise.all) ketma-ketdan tezroq (vaqt ko'rsatilgan).
  • allSettled bilan ba'zisi xato bo'lsa ham davom etadi.
  • race bilan timeout ishlaydi.
  • Tarmoq/HTTP/timeout xatolari alohida boshqariladi.
  • UnhandledPromiseRejection yo'q (hammasi ushlangan).

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda JS'ning eng muhim mavzularidan birini — asinxron dasturlashni o'rgandik:

  • Sinxron (bloklaydi) vs asinxron (fonda, bloklamaydi — 0.5: event loop). JS bir thread'li, shuning uchun asinxronlik zarur.
  • Callback callback hell; Promise (pending fulfilled/rejected, .then/.catch/.finally) buni hal qildi.
  • async/await — eng o'qishli; async doim Promise qaytaradi, await kutadi (microtask — 2.9).
  • fetch 0.4-bob — Promise; res.ok tekshiring + res.json() (ikki await).
  • Kombinatorlar: Promise.all (parallel, hammasi), allSettled (har natija), race (birinchi/timeout), any.
  • Event loop: microtask (Promise) macrotask (setTimeout) dan oldin 2.9-bob.

Keyingi bob — 2.12-bob: Error handling — try/catch/finally, throw, custom error. Asinxron kodda try/catchni ishlatdik; endi xatolarni professional boshqarishni to'liq o'rganamiz. Xato — dasturning dushmani emas, balki uni mustahkam qiladigan vosita. To'g'ri error handling — havaskor va professional kodning eng aniq farqi.


Foydalanilgan rasmiy/ishonchli manbalar

  • MDN Web Docs — Using promises, async/await, Promise
  • MDN Web Docs — Promise.all/allSettled/race/any
  • MDN Web Docs — Microtask guide, event loop (microtask vs macrotask)

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
2.11-bob: Asinxron JS — callback, Promise, async/await, fetch — Wisar