IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq8/32-dars23 daqiqa
Mundarija (30)

Ketma-ket va parallel bajarish: await sikllari va Promise.all

Qisqacha: Mustaqil ishlarni birin-ketin await qilsangiz, vaqtlar qo'shiladi: uchta 300 ms li so'rov — 900 ms. Ularni avval hammasini boshlab, keyin birga kutsangiz — eng uzuni qadar, 300 ms: const [osh, manti] = await Promise.all([narxniSora("osh"), narxniSora("manti")]);. Bir-biriga bog'liq ishlar esa ketma-ket qoladi: for...of ichida await. forEach esa async callback'ni kutmaydi — bu eng ko'p uchraydigan tuzoq.

Bu darsda

  • Ketma-ket va parallel bajarishning vaqtdagi farqini o'lchab, tushuntira olasiz.
  • Mustaqil ishlarni Promise.all bilan parallel ishga tushirib, natijalarni tartibda olasiz.
  • Siklda await qachon to'g'ri (for...of) va qachon sekin ekanini ajrata olasiz.
  • forEach + async tuzog'ini tanib, uni for...of yoki map + Promise.all bilan tuzata olasiz.
  • Juda ko'p ishni partiyalarga bo'lib, cheklangan parallellik bilan bajara olasiz.

Oldin bilishingiz kerak: async / await, Promise asoslari, map.

1. Nega bu kerak?

«Bahor»da ofitsiant Otabek Dilshod akaning stolidan uchta buyurtma oldi: osh, manti va lag'mon. Oshxonada uchta oshpaz bor, har bir taom taxminan 5 daqiqada tayyor bo'ladi.

Otabek ikki xil ishlashi mumkin:

  • Ketma-ket. Oshni beradi, tayyor bo'lishini kutadi, keyin mantini beradi, kutadi, keyin lag'monni. Dilshod aka 15 daqiqa kutadi.
  • Parallel. Uchala buyurtmani birdaniga beradi. Uchta oshpaz bir vaqtda ishlaydi. 5 daqiqada uchalasi tayyor.

Ikkinchisi aniq yaxshi — taomlar bir-biriga bog'liq emas. Lekin kodda ko'pincha birinchisi yoziladi. Sababi — await ning qulayligi. async / await darsining oxirida shu xatoni ko'rdik:

js
const osh = await narxniSora("osh");
const manti = await narxniSora("manti");

Kod chiroyli va to'g'ri, lekin "manti" so'rovi "osh" javobini kutib turadi. Bugun buni qanday tuzatishni, qachon tuzatmaslik kerakligini va bu yo'ldagi tuzoqlarni ko'ramiz.

Ikki atamani aniqlab olamiz:

  • Ketma-ket bajarish (sequential) — keyingi ish oldingisi tugagandan keyin boshlanadi.
  • Parallel bajarish — bir nechta ish bir vaqtda kutiladi: hammasi boshlanadi, keyin hammasining tugashi kutiladi.

Diqqat: "Parallel" so'zi bu yerda kutish haqida. JavaScript kodi baribir bitta stekda, navbat bilan bajariladi (Sinxron va asinxron kod). Bir vaqtda ishlaydiganlar — host'dagi taymerlar, tarmoq so'rovlari, fayl o'qishlar. Inglizcha manbalarda buni ko'pincha "concurrent" deb aytishadi.

2. Ketma-ket va parallel: o'lchab ko'ramiz

2.1 Ketma-ket — vaqtlar qo'shiladi

Har narx so'rovi 300 ms davom etadi. Uchtasini ketma-ket so'raymiz va vaqtni Date.now() bilan o'lchaymiz (Taymerlar darsidagidek):

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const menyu = { osh: 35000, manti: 30000, "lag'mon": 28000 };
async function narxniSora(taom) {
  await kut(300);
  return menyu[taom];
}

async function ketmaKet() {
  const boshi = Date.now();
  const osh = await narxniSora("osh");
  const manti = await narxniSora("manti");
  const lagmon = await narxniSora("lag'mon");
  const ms = Date.now() - boshi;
  console.log(osh, manti, lagmon);
  console.log("Taxminan", Math.round(ms / 100) * 100, "ms");
}

ketmaKet();

Konsolda:

text
35000 30000 28000
Taxminan 900 ms

Har await funksiyani to'xtatadi va keyingi qator — keyingi so'rov — faqat javob kelgandan keyin boshlanadi. 300 + 300 + 300 = 900. Aniq millisekundlar har ishga tushirishda biroz farq qiladi (910, 928...) — shuning uchun yuzlikkacha yaxlitladik.

2.2 Avval boshlash, keyin kutish

Promise asoslari da muhim qoidani ko'rgan edik: Promise ishni boshqarmaydi, u ishni kuzatadi. Ish narxniSora("osh") chaqirilgan paytda boshlanadi, await paytida emas. Demak uchala so'rovni avval boshlab, keyin kutsak bo'ladi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const menyu = { osh: 35000, manti: 30000, "lag'mon": 28000 };
async function narxniSora(taom) {
  await kut(300);
  return menyu[taom];
}

async function birdaniga() {
  const boshi = Date.now();
  const oshChek = narxniSora("osh");
  const mantiChek = narxniSora("manti");
  const lagmonChek = narxniSora("lag'mon");
  const osh = await oshChek;
  const manti = await mantiChek;
  const lagmon = await lagmonChek;
  const ms = Date.now() - boshi;
  console.log(osh, manti, lagmon);
  console.log("Taxminan", Math.round(ms / 100) * 100, "ms");
}

birdaniga();

Konsolda:

text
35000 30000 28000
Taxminan 300 ms

Uch barobar tez! Uchta chaqiruv await siz — ular darhol uchta taymer qo'ydi va uchta "chek" (Promise) qaytardi. Uchala taymer bir vaqtda "tiqillaydi". Keyin biz cheklarni birin-ketin await qildik. Birinchisi 300 ms kutdi — shu orada qolgan ikkitasi ham tayyor bo'lib qoldi.

Ikki taomli qisqa variantda qadamma-qadam ko'ring. «Web API» ustuniga qarang — ikkala taymer bir vaqtda turibdi:

Agar 6-qatorni await dan keyin yozsak, manti taymeri faqat osh kelgach boshlanardi — jami 600 ms. Farq bitta narsada: ish qachon boshlanganida.

Vaqt chizig'ida solishtiring:

gantt
  dateFormat x
  axisFormat %L ms
  section Ketma-ket
    osh     :0, 300
    manti   :300, 600
    lag'mon :600, 900
  section Parallel
    osh     :0, 300
    manti   :0, 300
    lag'mon :0, 300

Yuqorida har so'rov oldingisi tugagach boshlanadi — chiziq zinapoyaga o'xshaydi. Pastda uchalasi bir chiziqdan, bir vaqtda boshlanadi.

O'lchovni grafikda ko'ring (beshta ishga tushirishning o'rtachasi):

Uchta 300 ms li so'rov: ketma-ket va parallel
  • Ketma-ket await300 + 300 + 300920 ms
  • Promise.alleng uzuni qadar306 ms

Manba: O'lchandi: Node 24.21, Windows 11, 5 marta ishga tushirish o'rtachasi (2026-10-05)

2.3 Lekin bu usulning tuzog'i bor

"Avval boshlab, keyin birin-ketin await" usuli to'g'ri ishlaydi — faqat hammasi muvaffaqiyatli bo'lsa. Ikkinchi so'rov birinchisidan oldin rad etilsa nima bo'ladi?

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function oshTayyorla() {
  await kut(300);
  return "osh";
}
async function mantiTayyorla() {
  await kut(100);
  throw new Error("Manti tugagan");
}

async function buyurtma() {
  try {
    const oshChek = oshTayyorla();
    const mantiChek = mantiTayyorla();
    console.log(await oshChek, await mantiChek);
  } catch (xato) {
    console.log("Ushlandi:", xato.message);
  }
}

buyurtma();

Node 24 da (fayl yo'li qisqartirildi):

text
file:///C:/.../11/tuzoq.mjs:10
  throw new Error("Manti tugagan");
        ^

Error: Manti tugagan
    at mantiTayyorla (file:///C:/.../11/tuzoq.mjs:10:9)

Node.js v24.21.0

"Ushlandi" chiqmadi — dastur to'xtadi! Sabab vaqtda. 100 ms da mantiChek rad etildi. Shu paytda funksiya await oshChek da turibdi — mantiChek ga hali hech kim "qaramagan": unga then ham, await ham ulanmagan. Node rad etilgan Promise'ning egasi yo'qligini ko'rdi va uni ushlanmagan rad etilish deb hisobladi (Promise asoslari). catch ga navbat 300 ms da kelardi, lekin dastur unga yetmadi.

Brauzer dasturni to'xtatmaydi: konsolga Uncaught (in promise) Error: Manti tugagan chiqadi, keyin catch ham ishlaydi. Lekin qizil xabar baribir "bu kodda muammo bor" degani.

Demak bir nechta Promise'ni birga kutish uchun maxsus vosita kerak — u hammasiga birdaniga qaraydi.

Tekshirib ko'ring: Uchta mustaqil so'rov: 200, 500 va 300 ms. Ketma-ket await va "avval boshlash" usulida jami taxminan qancha vaqt ketadi?

Javob

Ketma-ket — 200 + 500 + 300 = 1000 ms: vaqtlar qo'shiladi. Avval boshlasak — 500 ms: hammasi bir vaqtda kutiladi, jami vaqt eng uzunining vaqtiga teng.

3. Promise.all

3.1 Bir nechta Promise — bitta Promise

Promise.all(massiv) — Promise'lar massivini oladi va bitta Promise qaytaradi. U hammasi bajarilganda bajariladi, natijasi esa — natijalar massivi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const vaqt = { osh: 300, manti: 100, "lag'mon": 200 };

async function tayyorla(taom) {
  await kut(vaqt[taom]);
  console.log(`${taom} tayyor`);
  return `${taom} (stolga)`;
}

async function buyurtma() {
  const natijalar = await Promise.all([
    tayyorla("osh"),
    tayyorla("manti"),
    tayyorla("lag'mon"),
  ]);
  console.log(natijalar);
}

buyurtma();

Konsolda:

text
manti tayyor
lag'mon tayyor
osh tayyor
[ 'osh (stolga)', 'manti (stolga)', "lag'mon (stolga)" ]

Ikkita narsaga qarang:

  • Taomlar tayyor bo'lish tartibida chiqdi: manti (100 ms), lag'mon (200), osh (300). Uchalasi bir vaqtda ishladi.
  • Natijalar massivi esa so'ralgan tartibda: osh, manti, lag'mon. Promise.all har natijani o'z o'rniga qo'yadi — kim birinchi tugaganidan qat'i nazar.

Ikkinchisi juda qulay. Callbacklar darsida xuddi shu ishni qo'lda qilgan edik: qoldi hisoblagichi, javoblar[indeks] va qoldi === 0 tekshiruvi. Promise.all ularning hammasini o'zi bajaradi. O'shanda "buni bitta qator bajaradi" degan edik — mana o'sha qator.

Promise.all — Promise ning static metodi: u Promise ning o'ziga tegishli, alohida Promise'ga emas (Getter/setter va static a'zolar).

3.2 Destructuring bilan

Natijalar massiv bo'lgani uchun ularni destructuring bilan nomlash qulay:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const menyu = { osh: 35000, manti: 30000 };
async function narxniSora(taom) {
  await kut(300);
  return menyu[taom];
}

async function chekYoz() {
  const [osh, manti] = await Promise.all([
    narxniSora("osh"),
    narxniSora("manti"),
  ]);
  console.log(`Jami: ${osh + manti} so'm`);
}

chekYoz();

Konsolda:

text
Jami: 65000 so'm

Bu — eng ko'p uchraydigan shakl: "ikki-uch mustaqil narsani bir vaqtda olib, keyin birga ishlatish".

Ikki chekka holatni ham biling. Massiv bo'sh bo'lsa — Promise.all([]) darhol bo'sh massiv [] bilan bajariladi: kutadigan hech narsa yo'q. Massivda Promise bo'lmagan oddiy qiymat bo'lsa — u natijaga o'zgarmasdan tushadi: Promise.all([narxniSora("osh"), 5000]) → [35000, 5000]. Ikkinchisi qulay, lekin «Ko'p uchraydigan xatolar» da uning ayyor tomonini ko'ramiz.

Promise.all ga massivni "boshlangan ishlar ro'yxati" deb qarang. Ro'yxatga yozish paytida har ish allaqachon boshlangan bo'lishi kerak — ya'ni har element funksiya chaqiruvi.

3.3 Bitta rad etish — hammasi rad etiladi

Promise.all dagi Promise'lardan birortasi rad etilsa, Promise.all darhol — boshqalarini kutmasdan — o'sha xato bilan rad etiladi. «Avval boshlash» tuzog'idagi kodni Promise.all bilan qayta yozamiz:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function oshTayyorla() {
  await kut(300);
  console.log("osh baribir tayyor bo'ldi");
  return "osh";
}
async function mantiTayyorla() {
  await kut(100);
  throw new Error("Manti tugagan");
}

async function buyurtma() {
  try {
    const [osh, manti] = await Promise.all([
      oshTayyorla(),
      mantiTayyorla(),
    ]);
    console.log(osh, manti);
  } catch (xato) {
    console.log("Ushlandi:", xato.message);
  }
}

buyurtma();

Konsolda:

text
Ushlandi: Manti tugagan
osh baribir tayyor bo'ldi

Endi xato ushlandi va dastur to'xtamadi: Promise.all ikkala Promise'ga birdaniga ulanadi, shuning uchun hech biri "egasiz" qolmaydi.

Tartibga e'tibor bering: "Ushlandi" 100 ms da chiqdi — osh tayyor bo'lishini kutmasdan. Bu fail-fast ("tez yiqilish") xulqi: bitta qism yo'q bo'lsa, butun natija baribir kerak emas. Lekin "osh baribir tayyor bo'ldi" ham chiqdi. Promise.all boshqa ishlarni to'xtatmaydi — ular davom etadi, faqat natijasiga hech kim qaramaydi. Ishni haqiqatan bekor qilish uchun AbortController kerak.

Har xil vaziyat uchun boshqa birlashtiruvchilar ham bor: hammasining natijasi kerak bo'lsa (xato bo'lsa ham) — Promise.allSettled, birinchisi kifoya bo'lsa — Promise.race va Promise.any. Ularni keyingi darsda, Promise kombinatorlari da ko'ramiz.

Tekshirib ko'ring: Promise.all ga uchta Promise berildi: 300 ms da bajariladigan, 100 ms da rad etiladigan va 200 ms da rad etiladigan. catch qaysi xatoni oladi va qachon?

Javob

100 ms da rad etiladiganning xatosini, 100 ms da. Promise.all birinchi rad etishda darhol rad etiladi. 200 ms dagi ikkinchi rad etish e'tiborsiz qoladi — Promise.all allaqachon hal bo'lgan.

4. Sikl ichida await

4.1 for...of — ketma-ket

Siklda await yozsangiz, sikl har qadamda to'xtaydi — ishlar ketma-ket bajariladi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function tayyorla(taom) {
  await kut(100);
  return `${taom} tayyor`;
}

async function navbatBilan(taomlar) {
  const natijalar = [];
  for (const taom of taomlar) {
    const natija = await tayyorla(taom);
    console.log(natija);
    natijalar.push(natija);
  }
  console.log("Jami:", natijalar.length);
}

navbatBilan(["osh", "manti", "lag'mon"]);

Konsolda:

text
osh tayyor
manti tayyor
lag'mon tayyor
Jami: 3

Jami vaqt — taxminan 300 ms (100 + 100 + 100). Bu sekinroq, lekin ba'zan aynan shu kerak:

  • Har qadam oldingisiga bog'liq. Stol topilmasa, bron qilib bo'lmaydi.
  • Tartib muhim. To'lovlar ketma-ket yechilsin: birinchisi hisobni kamaytirgandan keyin ikkinchisi tekshirilsin.
  • Server chegarasi. Ko'p xizmatlar bir vaqtda kelgan juda ko'p so'rovni rad etadi (HTTP 429 — "juda ko'p so'rov"). Ketma-ket so'rash — eng xavfsiz yo'l.

for...of ichidagi await — to'g'ri ishlaydi: sikl haqiqatan kutadi. for, while va do...while ham shunday. Muammo faqat mustaqil ishlarni shunday yozganda — keraksiz kutish.

4.2 map + Promise.all — parallel

Mustaqil ishlar uchun boshqa shakl: map har element uchun ishni boshlaydi va Promise'lar massivini qaytaradi, Promise.all esa ularni birga kutadi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function tayyorla(taom) {
  await kut(100);
  return `${taom} tayyor`;
}

async function birdaniga(taomlar) {
  const natijalar = await Promise.all(taomlar.map(tayyorla));
  console.log(natijalar);
}

birdaniga(["osh", "manti", "lag'mon"]);

Konsolda:

text
[ 'osh tayyor', 'manti tayyor', "lag'mon tayyor" ]

Jami — taxminan 100 ms. taomlar.map(tayyorla) — [tayyorla("osh"), tayyorla("manti"), tayyorla("lag'mon")] bilan bir xil: uchta chaqiruv, uchta Promise, uchala ish boshlandi. Bu ikki qator — "massivdagi har element uchun asinxron ish, parallel" ning standart shakli.

Tekshirib ko'ring: 10 ta mustaqil ish, har biri 100 ms. for...of + await va map + Promise.all da jami taxminan qancha vaqt ketadi?

Javob

for...of + await — taxminan 1000 ms (10 × 100), har ish oldingisini kutadi. map + Promise.all — taxminan 100 ms, hammasi bir vaqtda boshlanadi.

4.3 forEach + async — tuzoq

Sardor yuqoridagini "qisqaroq" yozmoqchi bo'ldi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const vaqt = { osh: 300, manti: 100 };
async function tayyorla(taom) {
  await kut(vaqt[taom]);
  console.log(`${taom} tayyor`);
}
["osh", "manti"].forEach(async (taom) => {
  await tayyorla(taom);
});
console.log("Hammasi tayyor!");

Konsolda:

text
Hammasi tayyor!
manti tayyor
osh tayyor

"Hammasi tayyor!" birinchi chiqdi — hali hech narsa tayyor emas! Callback ichidagi await ishladi, lekin u faqat callback'ni to'xtatdi. forEach esa callback qaytargan Promise'ga umuman qaramaydi: u callback'ni chaqiradi, natijani tashlab yuboradi va keyingi elementga o'tadi. Qadamma-qadam kuzating:

Bu xato ikki karra yomon:

  • forEach dan keyingi kod natijalarni kutmaydi — "tayyor" deyilgan narsa hali tayyor emas.
  • Callback ichidagi xato hech qayerga yetmaydi. forEach atrofidagi try/catch uni ushlamaydi — callback'ning Promise'i "suzib yuradi" (async / await).

Massivning boshqa metodlari — map, filter, some, reduce — ham callback'ning Promise'ini kutmaydi: ular sinxron. Lekin map foydali, chunki u Promise'larni massivga yig'ib qaytaradi — ularni Promise.all ga berish mumkin. Ammo forEach hech narsa qaytarmaydi. Va filter bilan yana bir tuzoq bor: async predikat doim Promise qaytaradi, Promise esa truthy — hamma element "o'tadi".

Tuzatish — maqsadga qarab:

  • ketma-ket kerak — for (const taom of taomlar) { await tayyorla(taom); };
  • parallel kerak — await Promise.all(taomlar.map(tayyorla));.

Tekshirib ko'ring: Sardor forEach atrofiga try/catch qo'ydi, tayyorla esa ba'zan xato tashlaydi. catch bu xatoni ushlaydimi? Tuzatilgan Promise.all variantida-chi?

Javob

Asl kodda — yo'q. forEach sinxron tugaydi va try dan chiqiladi; xato keyinroq, hech kim kutmagan callback Promise'ida chiqadi — ushlanmagan rad etilish. Tuzatilgan variantda — ha: await funksiyani try ichida to'xtatadi, Promise.all esa birinchi xato bilan rad etiladi va u catch ga tushadi.

4.4 Eski usul: reduce bilan zanjir

Eski kodda ketma-ket bajarishni reduce bilan yozilganini uchratishingiz mumkin:

js
await taomlar.reduce(
  (oldingi, taom) => oldingi.then(() => tayyorla(taom)),
  new Promise((resolve) => resolve()),
);

U Promise zanjiri ni sikl bilan quradi: har element oldingi Promise'ning then iga ulanadi. Ishlaydi, lekin o'qish qiyin. async/await paydo bo'lgandan beri for...of + await aniqroq. Ikki satr bilan tanib qoling, o'zingiz yozmang.

Vaqt o'tib birin-ketin keladigan ma'lumotlar uchun maxsus sikl ham bor — for await...of. U Asinxron iteratsiya darsida, hozir bilish shart emas.

5. Qachon qaysi biri

5.1 Qaror jadvali

Vaziyat Usul Misol
keyingi ish oldingisining natijasiga muhtoj ketma-ket await stol → bron → chek
ishlar mustaqil, soni oz Promise.all osh va manti narxi
tartib yoki server chegarasi muhim for...of + await to'lovlarni birin-ketin yechish
ishlar mustaqil, lekin juda ko'p partiyalab 1000 ta mijozga SMS

Oxirgi qator yangi. Mingta so'rovni birdaniga Promise.all bilan yuborsangiz, server ularni "hujum" deb qabul qilishi, kompyuter esa xotirasi to'lib qolishi mumkin. Ketma-ket yuborsangiz — juda sekin. O'rtasi — partiyalab (batch): masalan, har safar 10 tadan parallel, partiyalar esa ketma-ket. Buni 3-mashqda yozasiz. Yanada aqlli usul — parallellik cheklovi (concurrency limit): "bir vaqtda ko'pi bilan N ta ish ketsin". Uni Asinxron naqshlar darsida yozamiz.

5.2 Aralash: qisman bog'liq ishlar

Hayotda ko'pincha ikkalasi aralash bo'ladi. «Bahor» kassasi chek chiqarishi uchun stolni topishi kerak, keyin shu stolning buyurtmasi va chegirma kartasi bir vaqtda olinsin:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function stolTop(odam) {
  await kut(100);
  return 5;
}
async function buyurtmaniOl(stol) {
  await kut(200);
  return { stol, summa: 65000 };
}
async function kartaniOl(stol) {
  await kut(200);
  return { stol, chegirma: 10 };
}

async function chekChiqar() {
  const boshi = Date.now();
  const stol = await stolTop(4);
  const [buyurtma, karta] = await Promise.all([
    buyurtmaniOl(stol),
    kartaniOl(stol),
  ]);
  const jami = buyurtma.summa * (1 - karta.chegirma / 100);
  const ms = Math.round((Date.now() - boshi) / 100) * 100;
  console.log(`${stol}-stol: ${jami} so'm, taxminan ${ms} ms`);
}

chekChiqar();

Konsolda:

text
5-stol: 58500 so'm, taxminan 300 ms

Avval stol — undan keyingi ikki ish shunga bog'liq. Keyin ikki mustaqil ish — parallel. Jami 100 + 200 = 300 ms. Hammasi ketma-ket bo'lsa — 500 ms bo'lardi. Qoida: har await oldida o'zingizdan so'rang — "keyingi qator haqiqatan shu natijaga muhtojmi?".

Tekshirib ko'ring: Sahifa ochilganda uchta narsa yuklanadi: foydalanuvchi profili, «Bahor» menyusi va ob-havo. Menyu va ob-havo profilga bog'liq emas, lekin "Sevimli taomlar" bloki profil va menyuga bog'liq. Qanday tashkil qilasiz?

Javob

Uchalasini birga boshlash mumkin: const [profil, menyu, havo] = await Promise.all([...]). Ularning hech biri boshqasiga bog'liq emas. "Sevimli taomlar" esa Promise.all dan keyin, ikkala natija tayyor bo'lgach hisoblanadi. Jami vaqt — uchtadan eng uzunining vaqti.

6. Ko'p uchraydigan xatolar

6.1 Mustaqil ishlarni ketma-ket await qilish

Darsning asosiy mavzusi: "kod ishlayapti, lekin sekin". Bu xato hech qachon xato xabarini bermaydi — faqat sahifa sekin ochiladi. Belgisi: ketma-ket await qatorlari, va keyingi qator oldingi natijani ishlatmaydi. Tuzatish: Promise.all.

6.2 Bog'liq ishlarni parallel qilish

Teskari xato. Sardor "hammasini parallel qilsam tezroq" deb o'yladi:

js
const [stol, bron] = await Promise.all([stolTop(4), bronQil(stol)]);

bronQil(stol) chaqirilganda stol hali yo'q — kod ReferenceError beradi, let stol oldinroq e'lon qilingan bo'lsa esa undefined bilan bron qiladi. Tuzatish: bog'liq qadam — ketma-ket.

6.3 Promise.all ga funksiyalarni berish

js
async function narxniSora() {
  return 35000;
}

async function chekYoz() {
  const natija = await Promise.all([narxniSora, narxniSora]);
  console.log(natija);
}

chekYoz();

Konsolda:

text
[ [AsyncFunction: narxniSora], [AsyncFunction: narxniSora] ]

Narxlar o'rniga funksiyalarning o'zi qaytdi! Promise.all ga chaqirilmagan funksiyalar berildi — ish umuman boshlanmadi. Promise.all Promise bo'lmagan qiymatni "tayyor natija" deb o'tkazib yuboradi, xato ham bermaydi. Tuzatish: qavslar — Promise.all([narxniSora(), narxniSora()]). Promise.all ishni boshlamaydi, faqat boshlangan ishlarni kutadi.

6.4 Natijalarni parallel ishlarning ichida chiqarish

Parallel ishlar ichida console.log qilsangiz yoki umumiy massivga push qilsangiz, tartib tugash tartibida bo'ladi — u har safar boshqacha bo'lishi mumkin (haqiqiy tarmoqda — albatta). Tuzatish: natijalarni Promise.all qaytargan massivdan oling va bitta joyda chiqaring — u doim so'ralgan tartibda.

Tekshirib ko'ring: Quyidagi kod nechta ishni parallel boshlaydi va natija nima bo'ladi? const natija = await Promise.all([narxniSora("osh"), narxniSora, 5000]); (narxniSora("osh") — 35000 qaytaradi.)

Javob

Faqat bitta — narxniSora("osh"). Ikkinchi element — chaqirilmagan funksiya, uchinchisi — oddiy son; ikkalasi ham "tayyor qiymat" sifatida o'tkaziladi. Natija: [35000, [AsyncFunction: narxniSora], 5000]. Xato xabari chiqmaydi — shuning uchun bu xatoni ko'z bilan qidirish kerak: Promise.all massivida har element (...) bilan tugashi kerak.

7. Mashqlar

1-mashq (oson): Tezlashtiring

Sardorning kodi uchta mustaqil narxni ketma-ket so'raydi — taxminan 600 ms. Uni Promise.all va destructuring bilan qayta yozing, natija o'zgarmasin, vaqt esa taxminan 200 ms bo'lsin.

js
async function menyuniKorsat() {
  const osh = await narxniSora("osh");
  const manti = await narxniSora("manti");
  const choy = await narxniSora("ko'k choy");
  console.log(`Osh ${osh}, manti ${manti}, choy ${choy}`);
}
Yechim
js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
const menyu = { osh: 35000, manti: 30000, "ko'k choy": 5000 };
async function narxniSora(taom) {
  await kut(200);
  return menyu[taom];
}

async function menyuniKorsat() {
  const boshi = Date.now();
  const [osh, manti, choy] = await Promise.all([
    narxniSora("osh"),
    narxniSora("manti"),
    narxniSora("ko'k choy"),
  ]);
  console.log(`Osh ${osh}, manti ${manti}, choy ${choy}`);
  const ms = Math.round((Date.now() - boshi) / 100) * 100;
  console.log("Taxminan", ms, "ms");
}

menyuniKorsat();

Konsolda:

text
Osh 35000, manti 30000, choy 5000
Taxminan 200 ms

Uchala so'rov bir vaqtda boshlandi. Destructuring natijalarga massiv tartibida nom berdi — Promise.all uni so'ralgan tartibda qaytaradi.

2-mashq (o'rta): forEach tuzog'ini tuzating

Kod har stol uchun bron qiladi, keyin hisobot chiqaradi. Lekin hisobot bo'sh chiqyapti. Ikki variantda tuzating: (a) bronlar ketma-ket — for...of; (b) parallel — map + Promise.all. Ikkalasida ham Bronlar: 1-stol, 2-stol, 3-stol chiqsin.

js
const bronlar = [];
[1, 2, 3].forEach(async (stol) => {
  const bron = await bronQil(stol); // 100 ms, "<stol>-stol" qaytaradi
  bronlar.push(bron);
});
console.log("Bronlar:", bronlar.join(", "));
Yechim
js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function bronQil(stol) {
  await kut(100);
  return `${stol}-stol`;
}

async function ketmaKet() {
  const bronlar = [];
  for (const stol of [1, 2, 3]) {
    bronlar.push(await bronQil(stol));
  }
  console.log("Bronlar:", bronlar.join(", "));
}

async function parallel() {
  const bronlar = await Promise.all([1, 2, 3].map(bronQil));
  console.log("Bronlar:", bronlar.join(", "));
}

async function asosiy() {
  await ketmaKet();
  await parallel();
}

asosiy();

Konsolda:

text
Bronlar: 1-stol, 2-stol, 3-stol
Bronlar: 1-stol, 2-stol, 3-stol

Asl kodda forEach callback'larni boshlab, darhol tugadi — console.log bo'sh massivni chiqardi. Ketma-ket variant ~300 ms, parallel — ~100 ms. Parallel variantda push yo'q: natijalar Promise.all dan to'g'ri tartibda keladi. asosiy dagi ikki await esa ikkala variant chiqishi aralashib ketmasligi uchun.

3-mashq (qiyin): Partiyalab bajarish

«Bahor» 7 ta doimiy mehmonga "Yangi taom: norin!" SMS'ini yubormoqchi. SMS xizmati bir vaqtda ko'pi bilan 3 ta so'rov qabul qiladi. Shu sababli kurs/mashqlar/11/08-parallel/partiyalab.mjs faylida partiyalab(elementlar, hajm, ish) funksiyasini yozing: elementlarni hajm tadan partiyalarga bo'lsin, har partiyani Promise.all bilan parallel, partiyalarni esa ketma-ket bajarsin va hamma natijalarni bitta massivda, asl tartibda qaytarsin. Har partiya boshida N-partiya: ... deb yozsin.

Ishora: partiya — elementlar.slice(i, i + hajm) (slice), i esa hajm qadam bilan o'sadigan for sikli; natijalarni push(...partiyaNatijasi) bilan yig'ing.

Yechim
js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
async function smsYubor(ism) {
  await kut(100);
  return `${ism}: yuborildi`;
}

async function partiyalab(elementlar, hajm, ish) {
  const natijalar = [];
  for (let i = 0; i < elementlar.length; i += hajm) {
    const partiya = elementlar.slice(i, i + hajm);
    console.log(`${i / hajm + 1}-partiya: ${partiya.join(", ")}`);
    const javoblar = await Promise.all(partiya.map(ish));
    natijalar.push(...javoblar);
  }
  return natijalar;
}

const mehmonlar = ["Dilshod", "Malika", "Bobur", "Aziz",
  "Nodira", "Dilnoza", "Kamola"];

async function asosiy() {
  const boshi = Date.now();
  const natijalar = await partiyalab(mehmonlar, 3, smsYubor);
  console.log(natijalar.length, "ta SMS:", natijalar.at(-1));
  const ms = Math.round((Date.now() - boshi) / 100) * 100;
  console.log("Taxminan", ms, "ms");
}

asosiy();

Konsolda:

text
1-partiya: Dilshod, Malika, Bobur
2-partiya: Aziz, Nodira, Dilnoza
3-partiya: Kamola
7 ta SMS: Kamola: yuborildi
Taxminan 300 ms

7 ta SMS uchta partiyada: 3 + 3 + 1. Har partiya ~100 ms — jami ~300 ms. Ketma-ket bo'lsa — 700 ms, hammasi birdaniga bo'lsa — 100 ms, lekin xizmat chegarasini buzardi.

Zaif joyi: har partiya o'zining eng sekin elementini kutadi. Bitta SMS 2 soniya osilib qolsa, qolgan ikkita "joy" shu vaqt bo'sh turadi. Bo'shagan joyga darhol keyingisini qo'yadigan usul (parallellik cheklovi) — Asinxron naqshlar da.

bash
git add 11/08-parallel/partiyalab.mjs
git commit -m "11/08: partiyalab — cheklangan parallel bajarish"

8. Real ishda

  • Sahifa yuklanishi. Dashboard ochilganda profil, bildirishnomalar va statistika — uchta mustaqil so'rov. Ularni Promise.all bilan yuklash sahifani sezilarli tezlashtiradi. Ketma-ket "sharshara" (waterfall) so'rovlar — tezlik auditlarida birinchi topiladigan muammo; DevTools'ning Network panelida ular zinapoya bo'lib ko'rinadi. Tarmoq so'rovlarini fetch asoslari da boshlaymiz.
  • Backend. Bitta API javobi uchun bazadan uch jadvalni o'qish — Promise.all. Lekin bitta tranzaksiya ichidagi so'rovlar — ketma-ket.
  • Node skriptlari. Papkadagi 50 ta faylni o'qish yoki o'lchamini o'zgartirish: for...of + await bilan har fayl oldingisini kutadi, Promise.all bilan esa hammasi birga o'qiladi. Lekin bir vaqtda ochiq fayllar sonining ham chegarasi bor — minglab fayl bo'lsa, yana partiyalab ishlanadi.
  • Ommaviy ishlar. Minglab rasmlarni qayta ishlash, SMS/email yuborish — partiyalab yoki parallellik cheklovi bilan. Buning tayyor kutubxonalari bor (p-limit kabi) — npm'ni 16-qismda o'rganamiz, hozir bilish shart emas.
  • Intervyu. "Bu kodni qanday tezlashtirasiz?" (ketma-ket await lar), "forEach ichida await ishlaydimi?", "Promise.all bitta xatoda nima qiladi?" — juda tez-tez beriladi.

Xulosa

  • Ketma-ket await — vaqtlar qo'shiladi; parallel — jami vaqt eng uzun ish qadar.
  • Ish Promise yaratilganda boshlanadi: avval hammasini boshlang, keyin Promise.all bilan birga kuting.
  • Promise.all — natijalar so'ralgan tartibda; birinchi rad etishda darhol rad etiladi, qolgan ishlarni to'xtatmaydi.
  • for...of + await — ketma-ket (bog'liq qadamlar, tartib, server chegarasi); map + Promise.all — parallel.
  • forEach (va filter, some) async callback'ni kutmaydi — natijalar va xatolar yo'qoladi.
  • Juda ko'p ish — partiyalab yoki parallellik cheklovi bilan.

Keyingi dars: Promise kombinatorlari — Promise.all ning "qarindoshlari": hammasining natijasi kerak bo'lsa allSettled, birinchisi yetsa race va any — va har birini qaysi vaziyatda tanlash.

Manbalar

  • MDN: "Promise.all()", "async function" (Sequential vs concurrent), "Array.prototype.forEach()" (async callback eslatmasi) — developer.mozilla.org
  • ECMAScript spetsifikatsiyasi: "Promise.all" — tc39.es/ecma262
  • web.dev: "Avoid chaining critical requests" (so'rov "sharshara"si) — web.dev
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Ketma-ket va parallel bajarish: await sikllari va Promise.all — IlmHamroh