17-bo‘lim

Asinxron kod

Promise turlari, Promise.all kortejlari, forEach ning kutmasligi va async funksiyaning void ga mosligi.

🕑 22 daqiqa o‘qish 📄 683 so‘z 👁 0 marta ko‘rilgan
Ushbu bo‘lim mundarijasi
  1. Promise<T> va await
  2. Unutilgan await
  3. async doim Promise qaytaradi
  4. Ichma-ich Promise yassilanadi
  5. Promise.all
  6. Promise.allSettled
  7. Kutilmagan narsa: forEach kutmaydi
  8. Ikkinchi ko'rinishi: fire-and-forget
  9. catch dagi xato
  10. Asinxron oqimlar
  11. Xulosa

Asinxron kod - xatolar eng ko'p yashirinadigan joy. TypeScript ularning bir qismini tutadi, bir qismini esa - biz ko'rgan void qoidasi tufayli - o'tkazib yuboradi.

Bu bobda ikkalasini ham ko'ramiz.

Promise<T> va await #

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

async function narxi(nomi: string): Promise<number> {
  await kutish(10);
  return nomi === "Olma" ? 12000 : 0;
}

const n = await narxi("Olma");
console.log(n.toFixed(2));

export {};
Terminal
tsx src/asos.ts
Natija
12000.00

async funksiya Promise<number> qaytaradi, await esa undan number ni ajratib oladi.

Unutilgan await #

Bu - asinxron koddagi eng ko'p uchraydigan xato, va TypeScript uni tutadi:

TS
async function narxi(): Promise<number> {
  return 12000;
}

const n = narxi();
console.log(n.toFixed(2));

export {};
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/unut.ts(6,15): error TS2339: Property 'toFixed' does not exist on
type 'Promise<number>'.

Oddiy JavaScript da bu undefined beradi va sabab ancha keyin ma'lum bo'lardi.

async doim Promise qaytaradi #

TS
async function son(): Promise<number> {
  return 42;
}

const x = son();
console.log(x instanceof Promise, await x);

export {};
Terminal
tsx src/doim.ts
Natija
true 42

return 42 yozildi, lekin Promise<number> chiqdi. Shuning uchun async function f(): number deb yozib bo'lmaydi - qaytish turi doim Promise bilan o'ralgan bo'lishi kerak.

Ichma-ich Promise yassilanadi #

TS
async function ichki(): Promise<number> {
  return 12000;
}

async function tashqi(): Promise<number> {
  return ichki();
}

type A = Awaited<Promise<Promise<number>>>;
const a: A = 5;

const n: number = await tashqi();
console.log(n, a);

export {};
Terminal
tsx src/yassi.ts
Natija
12000 5

tashqi Promise<number> qaytardi - Promise<Promise<number>> emas. Awaited<T> ham xuddi shunday: u qancha qatlam bo'lsa ham yechib tashlaydi.

Shuning uchun await await f() yozishning ma'nosi yo'q.

Promise.all #

TS
async function nomi(): Promise<string> {
  return "Olma";
}
async function narx(): Promise<number> {
  return 12000;
}
async function bor(): Promise<boolean> {
  return true;
}

const [n, p, b] = await Promise.all([nomi(), narx(), bor()]);
console.log(n.toUpperCase(), p.toFixed(0), b === true);

export {};
Terminal
tsx src/all.ts
Natija
OLMA 12000 true

Uchta turli tur - va har biri o'z turini saqladi. Promise.all kortej (4-bob) qaytaradi, (string | number | boolean)[] emas.

Xatoni ham tutadi:

TS
async function nomi(): Promise<string> {
  return "Olma";
}
async function narx(): Promise<number> {
  return 12000;
}

const [n, p] = await Promise.all([nomi(), narx()]);
console.log(n.toFixed(0));

export {};
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/all2.ts(9,15): error TS2551: Property 'toFixed' does not exist on
type 'string'. Did you mean 'fixed'?

Promise.allSettled #

Promise.all bitta xato bo'lsa darhol to'xtaydi. Hammasini kutish kerak bo'lsa:

TS
async function yaxshi(): Promise<number> {
  return 12000;
}
async function yomon(): Promise<number> {
  throw new Error("tarmoq xatosi");
}

const natijalar = await Promise.allSettled([yaxshi(), yomon()]);

for (const r of natijalar) {
  if (r.status === "fulfilled") console.log("ok:", r.value);
  else console.log("xato:", (r.reason as Error).message);
}

export {};
Terminal
tsx src/settled.ts
Natija
ok: 12000
xato: tarmoq xatosi

r.status === "fulfilled" - bu 6-bobdagi ajratilgan union. status ni tekshirmasdan r.value ga murojaat qilib bo'lmaydi.

MetodXatoda
Promise.allDarhol rad etadi
Promise.allSettledHammasini kutadi, holatini beradi
Promise.raceBirinchi tugaganini oladi
Promise.anyBirinchi muvaffaqiyatlisini oladi

Kutilmagan narsa: forEach kutmaydi #

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

const royxat: string[] = [];

async function qosh(nomi: string): Promise<void> {
  await kutish(10);
  royxat.push(nomi);
}

["Olma", "Anor"].forEach(async (n) => {
  await qosh(n);
});

console.log("forEach dan keyin:", royxat.length);
await kutish(60);
console.log("kutgandan keyin:", royxat.length);

export {};
Terminal
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/foreach.ts
Natija
tsc: xato yo_q
forEach dan keyin: 0
kutgandan keyin: 2

forEach dan keyin ro'yxat bo'sh. Ishlar keyinroq bajarildi.

Sabab 5-bobdagi qoidada: forEach void qaytaradigan funksiya kutadi, async funksiya esa Promise<void> qaytaradi. void esa qaytarilgan qiymatga qiziqmaydi - shu sababli Promise e'tiborsiz qoladi.

tsc hech narsa demadi, chunki qoida bo'yicha hammasi to'g'ri.

Ro'yxat ustida asinxron ish: uch yo'l forEach(async ...) - kutmaydi Olma Anor Kod darhol davom etadi - natija hali tayyor emas royxat.length === 0 tsc jim for...of + await - ketma-ket Olma Anor Har biri oldingisini kutadi - umumiy vaqt yig'iladi Tartib muhim bo'lganda Masalan: har bir yozuvni navbat bilan bazaga yozish Promise.all(map(...)) - parallel Olma Anor Birga boshlanadi, hammasi tugashi kutiladi royxat.length === 2, eng tez yo'l Diqqat: 1000 ta element - 1000 ta bir vaqtdagi so'rov
forEach jimgina noto'g'ri; qolgan ikkitasi maqsadga qarab tanlanadi

Ketma-ket kerak bo'lsa:

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

const royxat: string[] = [];

async function qosh(nomi: string): Promise<void> {
  await kutish(10);
  royxat.push(nomi);
}

for (const n of ["Olma", "Anor"]) {
  await qosh(n);
}

console.log("for...of dan keyin:", royxat.length);

export {};
Terminal
tsx src/forof.ts
Natija
for...of dan keyin: 2

Parallel kerak bo'lsa:

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

const royxat: string[] = [];

async function qosh(nomi: string): Promise<void> {
  await kutish(10);
  royxat.push(nomi);
}

await Promise.all(["Olma", "Anor"].map(qosh));

console.log("Promise.all dan keyin:", royxat.length);

export {};
Terminal
tsx src/map.ts
Natija
Promise.all dan keyin: 2
Bu xatoni qanday tutish mumkin

TypeScript ning o'zi tutmaydi, lekin ESLint tutadi:

QoidaNima qiladi
no-misused-promisesforEach(async ...) ni taqiqlaydi
no-floating-promisesawait siz qoldirilgan Promise ni topadi

Ikkalasi ham typescript-eslint ning turga asoslangan qoidalari. Ular ishlashi uchun ESLint sozlamasida parserOptions.project ko'rsatilgan bo'lishi kerak.

Asinxron kod yozadigan har bir loyihada bu ikki qoida yoqilgan bo'lishi kerak.

Ikkinchi ko'rinishi: fire-and-forget #

Xuddi shu qoida qayta chaqiruvlarda ham ishlaydi:

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

type Ishlov = (nomi: string) => void;

const f: Ishlov = async (nomi) => {
  await kutish(10);
  console.log("ishlov tugadi:", nomi);
};

f("Olma");
console.log("darhol");
await kutish(60);

export {};
Terminal
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/void.ts
Natija
tsc: xato yo_q
darhol
ishlov tugadi: Olma

f("Olma") chaqirildi, lekin natija kutilmadi - "darhol" avval chop etildi.

Ba'zan bu aynan kerak (jurnalga yozish, tahlil yuborish). Ko'pincha esa - xato. Farqni faqat siz bilasiz, shuning uchun void f("Olma") deb ataylab yozish odat qilingan: bu "bilaman, kutmayapman" degani.

catch dagi xato #

TS
async function yomon(): Promise<number> {
  throw new Error("tarmoq xatosi");
}

try {
  await yomon();
} catch (e) {
  console.log(e.message);
}

export {};
Terminal
tsc --noEmit 2>&1 | fold -sw 74
Natija
src/catch.ts(8,15): error TS18046: 'e' is of type 'unknown'.

15-bobdagi qoida bu yerda ham amal qiladi: rad etilgan Promise ham istalgan qiymat bilan rad etilishi mumkin.

Ko'p hollarda istisno o'rniga natija turini qaytarish qulayroq:

TS
type Natija<T> =
  | { ok: true; qiymat: T }
  | { ok: false; xato: string };

async function narxi(nomi: string): Promise<Natija<number>> {
  if (nomi !== "Olma") return { ok: false, xato: "topilmadi" };
  return { ok: true, qiymat: 12000 };
}

for (const n of ["Olma", "Anor"]) {
  const r = await narxi(n);
  console.log(r.ok ? r.qiymat : r.xato);
}

export {};
Terminal
tsx src/natija.ts
Natija
12000
topilmadi

Endi xato turda ko'rinadi va chaqiruvchi uni e'tiborsiz qoldira olmaydi. Istisnolar esa turlarda umuman ko'rinmaydi.

Asinxron oqimlar #

TS
function kutish(ms: number): Promise<void> {
  return new Promise((bajar) => setTimeout(bajar, ms));
}

async function* oqim(): AsyncGenerator<number> {
  for (const n of [1, 2, 3]) {
    await kutish(5);
    yield n * 1000;
  }
}

for await (const n of oqim()) {
  console.log(n);
}

export {};
Terminal
tsx src/oqim.ts
Natija
1000
2000
3000

AsyncGenerator<number> va for await...of - katta ma'lumotni bo'lak-bo'lak o'qish uchun: fayl qatorlari, baza sahifalari, tarmoq oqimi.

Xulosa #

  • async funksiya doim Promise<T> qaytaradi; qaytish turi ham shunday yozilishi kerak.
  • Unutilgan await TS2339 bilan tutiladi - qiymat Promise<T> bo'lib qoladi.
  • Ichma-ich Promise lar yassilanadi; Awaited<T> barcha qatlamlarni yechadi.
  • Promise.all kortej qaytaradi - har bir element o'z turini saqlaydi.
  • Promise.allSettled ajratilgan union beradi: status ni tekshirmasdan natijaga kirish mumkin emas.
  • forEach(async ...) kutmaydi va tsc bu haqda hech narsa demaydi - sabab void qoidasi.
  • Ketma-ket kerak bo'lsa for...of + await, parallel kerak bo'lsa Promise.all(map(...)).
  • typescript-eslint ning no-misused-promises va no-floating-promises qoidalari bu xatolarni tutadi.
  • Ataylab kutmayotgan bo'lsangiz, void f() deb yozing.
  • Rad etish qiymati ham unknown; istisno o'rniga natija turini qaytarish xatoni turda ko'rinadigan qiladi.
  • AsyncGenerator va for await...of - bo'lak-bo'lak o'qish uchun.
Amaliy topshiriq
  1. Promise<number> qaytaradigan async funksiya yozing va uni await bilan chaqiring.
  2. await ni olib tashlab, TS2339 xabarini o'qing.
  3. Promise.all bilan uch xil turdagi natijani birga oling.
  4. Ulardan birini noto'g'ri ishlatib, xatoni tuting.
  5. Promise.allSettled bilan biri yiqiladigan ikkita ishni bajaring.
  6. forEach(async ...) yozib, ro'yxat bo'sh qolishini tasdiqlang.
  7. Uni for...of bilan tuzating.
  8. Keyin Promise.all(map(...)) variantini yozib, ikkalasining vaqtini solishtiring.
  9. catch (e) da e.message yozib, TS18046 ni oling va tuzating.
  10. O'sha funksiyani Natija<T> qaytaradigan qilib qayta yozing.

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.