17-bo‘lim
Asinxron kod
Promise turlari, Promise.all kortejlari, forEach ning kutmasligi va async funksiyaning void ga mosligi.
Ushbu bo‘lim mundarijasi
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 #
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 {};
tsx src/asos.ts
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:
async function narxi(): Promise<number> {
return 12000;
}
const n = narxi();
console.log(n.toFixed(2));
export {};
tsc --noEmit 2>&1 | fold -sw 74
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 #
async function son(): Promise<number> {
return 42;
}
const x = son();
console.log(x instanceof Promise, await x);
export {};
tsx src/doim.ts
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 #
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 {};
tsx src/yassi.ts
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 #
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 {};
tsx src/all.ts
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:
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 {};
tsc --noEmit 2>&1 | fold -sw 74
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:
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 {};
tsx src/settled.ts
ok: 12000
xato: tarmoq xatosi
r.status === "fulfilled" - bu 6-bobdagi ajratilgan union.
status ni tekshirmasdan r.value ga murojaat qilib
bo'lmaydi.
| Metod | Xatoda |
|---|---|
Promise.all | Darhol rad etadi |
Promise.allSettled | Hammasini kutadi, holatini beradi |
Promise.race | Birinchi tugaganini oladi |
Promise.any | Birinchi muvaffaqiyatlisini oladi |
Kutilmagan narsa: forEach kutmaydi #
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 {};
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/foreach.ts
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.
Ketma-ket kerak bo'lsa:
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 {};
tsx src/forof.ts
for...of dan keyin: 2
Parallel kerak bo'lsa:
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 {};
tsx src/map.ts
Promise.all dan keyin: 2
TypeScript ning o'zi tutmaydi, lekin ESLint tutadi:
| Qoida | Nima qiladi |
|---|---|
no-misused-promises | forEach(async ...) ni taqiqlaydi |
no-floating-promises | await 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:
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 {};
tsc --noEmit && echo "tsc: xato yo_q"
tsx src/void.ts
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 #
async function yomon(): Promise<number> {
throw new Error("tarmoq xatosi");
}
try {
await yomon();
} catch (e) {
console.log(e.message);
}
export {};
tsc --noEmit 2>&1 | fold -sw 74
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:
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 {};
tsx src/natija.ts
12000
topilmadi
Endi xato turda ko'rinadi va chaqiruvchi uni e'tiborsiz qoldira olmaydi. Istisnolar esa turlarda umuman ko'rinmaydi.
Asinxron oqimlar #
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 {};
tsx src/oqim.ts
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 #
asyncfunksiya doimPromise<T>qaytaradi; qaytish turi ham shunday yozilishi kerak.- Unutilgan
awaitTS2339bilan tutiladi - qiymatPromise<T>bo'lib qoladi. - Ichma-ich
Promiselar yassilanadi;Awaited<T>barcha qatlamlarni yechadi. Promise.allkortej qaytaradi - har bir element o'z turini saqlaydi.Promise.allSettledajratilgan union beradi:statusni tekshirmasdan natijaga kirish mumkin emas.forEach(async ...)kutmaydi vatscbu haqda hech narsa demaydi - sababvoidqoidasi.- Ketma-ket kerak bo'lsa
for...of+await, parallel kerak bo'lsaPromise.all(map(...)). typescript-eslintningno-misused-promisesvano-floating-promisesqoidalari 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. AsyncGeneratorvafor await...of- bo'lak-bo'lak o'qish uchun.
Promise<number>qaytaradiganasyncfunksiya yozing va uniawaitbilan chaqiring.awaitni olib tashlab,TS2339xabarini o'qing.Promise.allbilan uch xil turdagi natijani birga oling.- Ulardan birini noto'g'ri ishlatib, xatoni tuting.
Promise.allSettledbilan biri yiqiladigan ikkita ishni bajaring.forEach(async ...)yozib, ro'yxat bo'sh qolishini tasdiqlang.- Uni
for...ofbilan tuzating. - Keyin
Promise.all(map(...))variantini yozib, ikkalasining vaqtini solishtiring. catch (e)dae.messageyozib,TS18046ni oling va tuzating.- O'sha funksiyani
Natija<T>qaytaradigan qilib qayta yozing.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
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.