IlmHamroh
JavaScript Full-stack/11-qism. Asinxron JavaScript va tarmoq10/32-dars20 daqiqa
Mundarija (36)

Promise yordamchilari: Promise.resolve, withResolvers, Promise.try va promisify

Qisqacha: Har Promise uchun new Promise(...) yozish shart emas. Tayyor qiymatdan Promise — Promise.resolve(qiymat), tayyor xatodan — Promise.reject(xato). Callback'li funksiyani Promise'ga Node'da util.promisify(fn) o'raydi. Promise'ni tashqaridan, keyinroq hal qilish kerak bo'lsa — Promise.withResolvers() (ES2024). Funksiya sinxron xato tashlashi ham, Promise qaytarishi ham mumkin bo'lsa — Promise.try(fn) (ES2025) ikkalasini bitta catch ga yo'naltiradi.

Bu darsda

  • Promise.resolve va Promise.reject bilan tayyor Promise yasaysiz va ular qachon kerakligini bilasiz.
  • kut(ms) — "uxlab turish" funksiyasi qanday ishlashini oxirigacha tushunasiz va Node'ning tayyor variantini ishlatasiz.
  • Callback'li funksiyalarni util.promisify bilan va qo'lda Promise'ga o'raysiz, this tuzog'ini chetlab o'tasiz.
  • Promise.withResolvers bilan Promise'ni tashqaridan hal qilasiz: navbat va hodisani kutish naqshlari.
  • Promise.try sinxron xatoni ham Promise zanjiriga qanday olib kirishini tushuntira olasiz.

Oldin bilishingiz kerak: Promise asoslari, async / await, Promise kombinatorlari, Callbacklar va callback hell.

1. Nega bu kerak?

O'tgan darslarda har Promise'ni new Promise((resolve, reject) => ...) bilan yasadik. Bu konstruktor — eng umumiy vosita. Lekin kundalik kodda to'rtta vaziyat qayta-qayta uchraydi va har biriga new Promise yozish noqulay:

Vaziyat Tayyor vosita
Natija allaqachon bor (kesh), lekin funksiya Promise qaytarishi kerak Promise.resolve
Eski callback'li funksiyani await bilan ishlatmoqchisiz util.promisify
Promise'ni bir joyda yaratib, boshqa joyda hal qilish kerak Promise.withResolvers
Funksiya sinxron xato tashlashi ham mumkin Promise.try

Bugungi dars — shu "asboblar qutisi". Har asbob kichik, lekin real kodda ularni har kuni ko'rasiz. Xuddi oshxonadagi pichoq kabi: bitta katta pichoq bilan hamma narsani kesish mumkin, lekin non uchun alohida, sabzi uchun alohida pichoq ishni tezlashtiradi.

2. Promise.resolve va Promise.reject

2.1 Tayyor qiymatdan Promise

Promise asoslari darsida keshli narxniOl shunday yozilgan edi:

js
if (kesh.has(taom)) {
  return new Promise((resolve) => resolve(kesh.get(taom)));
}

Uning qisqa shakli — Promise.resolve(qiymat): berilgan qiymat bilan darhol bajarilgan Promise. Ma'nosi aynan bir xil:

js
const kesh = new Map([["osh", 35000]]);

function narxniOl(taom) {
  if (kesh.has(taom)) {
    return Promise.resolve(kesh.get(taom));
  }
  return new Promise((resolve) => {
    setTimeout(() => {
      kesh.set(taom, 28000);
      resolve(28000);
    }, 100);
  });
}

console.log("1. So'rovlar yuborildi");
narxniOl("osh").then((n) => console.log("3. Osh:", n));
narxniOl("lag'mon").then((n) => console.log("4. Lag'mon:", n));
console.log("2. Sinxron kod tugadi");

Konsolda:

text
1. So'rovlar yuborildi
2. Sinxron kod tugadi
3. Osh: 35000
4. Lag'mon: 28000

Osh keshda bor, lekin uning narxi ham sinxron koddan keyin chiqdi. Promise.resolve qaytargan Promise allaqachon bajarilgan, then esa doim microtask orqali ishlaydi. Funksiya natijasi qayerdan kelishidan qat'i nazar, bir xil shaklda va bir xil tartibda keladi.

2.2 Promise'ni qayta o'ramaydi

Promise.resolve ga Promise bersangiz-chi? U yangi Promise yasamaydi — o'shaning o'zini qaytaradi:

js
const chek = new Promise((resolve) => {
  setTimeout(resolve, 100, "osh");
});
console.log(Promise.resolve(chek) === chek); // true

Shuning uchun Promise.resolve(x) — "x Promise bo'lsa ham, bo'lmasa ham, Promise qil" degani. Kombinatorlar (Promise kombinatorlari) ham massivdagi har elementni shunday o'raydi — Promise.all([35000, chek]) shuning uchun ishlaydi. O'tgan darsdagi 3-mashqda hammasi funksiyasi aynan shu ishni qilgan edi.

Yana bir nozik detal: Promise.resolve then metodi bor har qanday obyektni Promise deb qabul qiladi. Bunday obyekt thenable ("then'i bor") deyiladi:

js
const thenable = {
  then(resolve) {
    resolve("osh");
  },
};

Promise.resolve(thenable).then((t) => console.log(t)); // osh

Oddiy obyekt, lekin unda then metodi bor. Promise.resolve uni chaqirdi, resolve("osh") esa haqiqiy Promise'ni bajardi. Eski kutubxonalarning (masalan, jQuery) Promise'ga o'xshash obyektlari shu yo'l bilan haqiqiy Promise'ga aylanadi. await thenable ham xuddi shunday ishlaydi.

2.3 Tayyor xatodan Promise

Promise.reject(xato) — berilgan sabab bilan darhol rad etilgan Promise. Ko'pincha funksiya boshidagi tekshiruvda ishlatiladi: noto'g'ri argument — kutishning ma'nosi yo'q.

js
function stolniBronQil(odam) {
  if (odam < 1 || odam > 20) {
    const xato = new Error("Odam soni 1–20 oralig'ida bo'lsin");
    return Promise.reject(xato);
  }
  return new Promise((resolve) => {
    setTimeout(resolve, 200, `${odam} kishilik stol bron qilindi`);
  });
}

stolniBronQil(0)
  .then((xabar) => console.log(xabar))
  .catch((xato) => console.log("Xato:", xato.message));

Konsolda:

text
Xato: Odam soni 1–20 oralig'ida bo'lsin

Nega oddiy throw emas? Chunki funksiya Promise qaytaradi deb va'da bergan. Chaqiruvchi xatoni .catch da kutadi. throw bo'lsa, xato sinxron otilib, .catch gacha yetmasdi — chaqiruvchiga yana try/catch kerak bo'lardi.

async funksiya ichida esa bu muammo yo'q: u yerdagi throw o'zi rad etilgan Promise'ga aylanadi (async / await). Shuning uchun Promise.reject ko'proq async bo'lmagan funksiyalarda uchraydi.

Tekshirib ko'ring: Promise.resolve(35000).then((n) => console.log(n)); console.log("salom"); — qaysi biri birinchi chiqadi?

Javob

Avval salom, keyin 35000. Promise darhol bajarilgan bo'lsa ham, then callback'i microtask navbatiga tushadi va sinxron kod tugagach ishlaydi.

3. kut(ms) — kutib turish

3.1 O'zimiz yozamiz

kut ni Promise zanjiri darsidan beri ishlatib kelyapmiz. Endi uni yordamchilar qatorida to'liq ko'rib chiqamiz. Ko'p tillarda sleep(ms) degan buyruq bor: "shuncha vaqt kut". JavaScript'da bunday sinxron buyruq yo'q — u stekni bloklagan bo'lardi (Sinxron va asinxron kod). Lekin Promise va setTimeout bilan bir qatorda yasaladi:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function sanoq() {
  for (const son of [3, 2, 1]) {
    console.log(son);
    await kut(200);
  }
  console.log("Osh dasturxonda!");
}

sanoq();

Konsolda:

text
3
2
1
Osh dasturxonda!

Har await kut(200) da sanoq 200 ms to'xtaydi, lekin stek bo'sh: shu orada boshqa kod bemalol ishlaydi. Bu bloklovchi kutish emas — funksiya o'zini "pauza" qiladi.

Dasturchilar bu funksiyani sleep, delay yoki wait deb atashadi. Kursda — kut. U real ishda qayta urinishlar orasida kutish uchun (Asinxron naqshlar), animatsiya qadamlari va testlarda kerak bo'ladi.

3.2 Node'ning tayyor varianti

Node'da kut ni yozish shart emas — node:timers/promises modulida setTimeout ning Promise varianti bor:

js
import { setTimeout as kut } from "node:timers/promises";

const natija = await kut(200, "choy damlandi");
console.log(natija); // choy damlandi

Ikkinchi argument — Promise bajarilganda beriladigan qiymat. import { setTimeout as kut } — import nomini o'zgartirish (ESM import): aks holda u global setTimeout bilan to'qnashardi. Bu yerda await funksiya ichida emas, modulning eng yuqorisida turibdi — Node .mjs faylda bunga ruxsat beradi. Uni Dinamik import() va top-level await darsida to'liq ko'ramiz.

Brauzerda bunday modul yo'q — u yerda o'zimizning kut ni ishlatamiz.

4. Promisify: callback'ni Promise'ga o'rash

4.1 Naqsh

Promisify ("Promise'ga aylantirish") — error-first callback'li funksiyani Promise qaytaradigan funksiyaga o'rash. Promise asoslari darsining 3-mashqida readFile ni qo'lda o'ragan edingiz. Naqsh doim bir xil:

flowchart LR
  C["Eski funksiya:<br/>fn(...args, callback)"] --> W["new Promise ichida<br/>chaqiriladi"]
  W --> E{"callback(xato, natija)"}
  E -- "xato bor" --> R["reject(xato)"]
  E -- "xato = null" --> S["resolve(natija)"]

Callback'ning ikki yo'li Promise'ning ikki yo'liga ulanadi. Har funksiya uchun buni qayta yozmaslik uchun umumiy yordamchi yasaymiz.

4.2 Umumiy promisifyQil

Callbacklar darsidagi narxniSora ni olamiz. Yordamchi istalgan error-first funksiyani o'raydi:

js
const menyu = { osh: 35000, manti: 30000 };

function narxniSora(taom, callback) {
  setTimeout(() => {
    if (!Object.hasOwn(menyu, taom)) {
      callback(new Error(`Menyuda yo'q: ${taom}`));
      return;
    }
    callback(null, menyu[taom]);
  }, 100);
}

function promisifyQil(fn) {
  return (...argumentlar) => {
    return new Promise((resolve, reject) => {
      fn(...argumentlar, (xato, natija) => {
        if (xato) {
          reject(xato);
          return;
        }
        resolve(natija);
      });
    });
  };
}

const narxniSoraP = promisifyQil(narxniSora);

async function narxlar() {
  console.log("Osh:", await narxniSoraP("osh"));
  try {
    await narxniSoraP("palov");
  } catch (xato) {
    console.log("Xato:", xato.message);
  }
}

narxlar();

Konsolda:

text
Osh: 35000
Xato: Menyuda yo'q: palov

Qatorma-qator:

  • promisifyQil(fn) — funksiya qaytaradigan funksiya (Funksiya — qiymat: callback va HOF). fn closure'da eslab qolinadi.
  • (...argumentlar) — rest parametr: narxniSoraP("osh") chaqirilganda argumentlar = ["osh"].
  • fn(...argumentlar, callback) — asl funksiya o'z argumentlari va oxirida bizning callback bilan chaqiriladi. Error-first kelishuvida callback doim oxirgi parametr (Callbacklar).
  • Callback ichida — xato bo'lsa reject, aks holda resolve.

P qo'shimchasi — "Promise varianti" degan belgi; real kodda ham readFileP, queryAsync kabi nomlar uchraydi.

4.3 Node'da tayyor: util.promisify

Node'da bu yordamchi standart kutubxonada bor — util.promisify:

js
import { promisify } from "node:util";
import { readFile } from "node:fs";

const faylniOqi = promisify(readFile);

try {
  await faylniOqi("bronlar.json", "utf8");
} catch (xato) {
  console.log("O'qib bo'lmadi:", xato.code); // O'qib bo'lmadi: ENOENT
}

bronlar.json yo'q — shuning uchun ENOENT ("bunday fayl yo'q", Callbacklar darsidan). util.promisify biznikidan aqlliroq: ba'zi funksiyalarning maxsus Promise varianti bor va u shuni topadi. Masalan, promisify(setTimeout) ham to'g'ri ishlaydi, garchi setTimeout da callback birinchi parametr bo'lsa ham.

Lekin Node'ning o'z fayl funksiyalari uchun promisify ham shart emas: tayyor Promise variantlari bor — node:fs/promises. Yangi kodda shuni ishlating:

js
import { readFile } from "node:fs/promises";

try {
  await readFile("bronlar.json", "utf8");
} catch (xato) {
  console.log("O'qib bo'lmadi:", xato.code); // O'qib bo'lmadi: ENOENT
}

Qoida: avval tayyor Promise API'ni qidiring (fs/promises, timers/promises, dns/promises). Topilmasa — util.promisify. U ham yaramasa (masalan, brauzerda) — qo'lda o'rang.

4.4 this tuzog'i

Metodni promisify qilganda this yo'qoladi — xuddi metodni callback sifatida berganda yo'qolgani kabi (this yo'qolishi va uni tiklash):

js
import { promisify } from "node:util";

class Kassa {
  constructor(nomi) {
    this.nomi = nomi;
  }
  chekYoz(summa, callback) {
    const kassa = this.nomi;
    setTimeout(() => callback(null, `${kassa}: ${summa} so'm`), 100);
  }
}

const kassa = new Kassa("1-kassa");

const xatoli = promisify(kassa.chekYoz);
try {
  await xatoli(35000);
} catch (xato) {
  console.log("Xato:", xato.message);
}

const togri = promisify(kassa.chekYoz.bind(kassa));
console.log(await togri(35000));

Konsolda:

text
Xato: Cannot read properties of undefined (reading 'nomi')
1-kassa: 35000 so'm

kassa.chekYoz — obyektdan uzib olingan funksiya. Promisify uni oddiy funksiya sifatida chaqiradi, this esa modulda undefined. Xabar: "undefined ning 'nomi' xususiyatini o'qib bo'lmadi". Tuzatish — bind(kassa): this ni oldindan bog'lash.

E'tibor bering: chekYoz ichidagi sinxron TypeError ham Promise'ning rad etilishiga aylandi. util.promisify asl funksiyani Promise ichida chaqiradi — ichidagi xato ham Promise yo'lidan keladi.

4.5 Promisify qachon yaramaydi

Promise bir marta hal bo'ladi. Shuning uchun promisify faqat bir martalik natija beradigan funksiyalarga mos:

  • setInterval yoki hodisa tinglovchisi callback'ni ko'p marta chaqiradi — Promise faqat birinchisini oladi, qolganlari yo'qoladi. Ko'p marta keladigan ma'lumot uchun boshqa vosita bor — Asinxron iteratsiya.
  • Error-first bo'lmagan callback (callback(natija) — xatosiz) — promisifyQil natijani xato deb o'ylaydi va reject qiladi. Bunday funksiyani qo'lda o'rang.

Tekshirib ko'ring: promisifyQil bilan o'ralgan funksiya ichidagi callback ikki marta chaqirilsa nima bo'ladi?

Javob

Hech narsa buzilmaydi: birinchi chaqiruv Promise'ni hal qiladi, ikkinchisidagi resolve yoki reject e'tiborsiz qoladi. Promise'ning "bir marta hal bo'lish" qoidasi callback'lardagi ikki marta chaqirish muammosini o'z-o'zidan hal qiladi.

5. Promise.withResolvers: tashqaridan hal qilish

5.1 Muammo: resolve ichkarida qamalgan

new Promise((resolve, reject) => ...) da resolve va reject faqat executor ichida mavjud. Ko'pincha esa Promise'ni hozir yaratish, lekin boshqa joyda, keyinroq hal qilish kerak bo'ladi.

«Bahor» oshxonasida: ofitsiant buyurtma beradi va chek oladi (Promise). Oshpaz esa taom tayyor bo'lganda alohida tugmani bosadi. Chekni yaratgan kod va uni hal qiladigan kod — ikki xil joyda.

Yillar davomida dasturchilar buni shunday aylanib o'tishgan:

js
let tayyorQil;
const chek = new Promise((resolve) => {
  tayyorQil = resolve;
});

chek.then((taom) => console.log("Olib chiqildi:", taom));
setTimeout(() => tayyorQil("osh"), 100);

Konsolda:

text
Olib chiqildi: osh

resolve ni tashqi o'zgaruvchiga "olib chiqdik". Ishlaydi, lekin noqulay: let, executor, qo'shimcha o'zgaruvchilar. Bu naqsh deferred ("kechiktirilgan") deb ataladi va eski kutubxonalarda tez-tez uchraydi.

5.2 Yangi usul

Promise.withResolvers() — Promise va uning ikki "tugmasi"ni bitta obyektda qaytaradi:

js
const { promise, resolve, reject } = Promise.withResolvers();

promise.then((taom) => console.log("Olib chiqildi:", taom));
setTimeout(() => resolve("osh"), 100);

Konsolda:

text
Olib chiqildi: osh

{ promise, resolve, reject } — destructuring: obyektdan uchta xususiyatni bir qatorda olish. Kod qisqardi va maqsad aniq: "menga Promise va uni boshqarish tugmalari kerak".

Promise.withResolvers ES2024 da qo'shilgan. Baseline ma'lumotiga ko'ra 2024-yil martidan hamma asosiy brauzerda bor (Chrome 119, Firefox 121, Safari 17.4), 2026-yil sentyabridan esa "keng tarqalgan" (widely available). Node'da 22-versiyadan bor.

5.3 Buyurtmalar navbati

Endi to'liq misol. Ofitsiant buyurtmaBer bilan Promise oladi, oshpaz esa oshpazAytdi bilan uni hal qiladi. Ular faqat kutilayotgan Mapi orqali bog'langan:

js
const kutilayotgan = new Map();

function buyurtmaBer(taom) {
  const { promise, resolve } = Promise.withResolvers();
  kutilayotgan.set(taom, resolve);
  console.log("Buyurtma:", taom);
  return promise;
}

function oshpazAytdi(taom) {
  const resolve = kutilayotgan.get(taom);
  if (resolve) {
    kutilayotgan.delete(taom);
    resolve(`${taom} tayyor`);
  }
}

buyurtmaBer("osh").then((xabar) => console.log(xabar));
buyurtmaBer("manti").then((xabar) => console.log(xabar));

setTimeout(() => oshpazAytdi("manti"), 100);
setTimeout(() => oshpazAytdi("osh"), 200);

Konsolda:

text
Buyurtma: osh
Buyurtma: manti
manti tayyor
osh tayyor

buyurtmaBer hech qanday taymer qo'ymaydi va oshxona haqida hech narsa bilmaydi. U faqat va'da beradi va "tugma"ni navbatga qo'yadi. Promise kim tomonidan va qachon hal qilinishini oshpazAytdi belgilaydi.

Real kodda bu naqsh ko'p joyda: WebSocket orqali yuborilgan so'rovga javob kelishini kutish (WebSocket va Server-Sent Events), boshqa oynadan xabar kutish, test kutubxonalarida "shu hodisa bo'lguncha kut".

Tekshirib ko'ring: Yuqoridagi kodda oshpazAytdi("lag'mon") chaqirilsa nima bo'ladi?

Javob

Hech narsa. kutilayotgan da lag'mon kaliti yo'q — get undefined qaytaradi va if (resolve) sharti bajarilmaydi. Bu tekshiruv bo'lmasa, undefined(...) chaqirilib, TypeError: resolve is not a function chiqardi.

6. Promise.try: sinxron xatoni ham ushlash

6.1 Muammo: ikki xil xato

Funksiya Promise qaytaradi deb o'ylaymiz va xatoni .catch bilan kutamiz. Lekin funksiya Promise qaytarishdan oldin, sinxron xato tashlasa-chi?

js
function menyuniOqi(matn) {
  const menyu = JSON.parse(matn);
  return Promise.resolve(menyu);
}

try {
  menyuniOqi("{buzuq").catch((x) => console.log("catch:", x.name));
} catch (xato) {
  console.log("try/catch ushladi:", xato.name);
}

Konsolda:

text
try/catch ushladi: SyntaxError

JSON.parse buzuq matnda sinxron SyntaxError tashladi (JSON). Funksiya Promise qaytarishga ulgurmadi — .catch gacha hech narsa yetmadi. Xatoni faqat tashqi try/catch ushladi. Demak bunday funksiyani chaqirgan kodga ikki himoya kerak: try/catch va .catch. Biri unutilsa — xato o'tib ketadi.

6.2 Yechim

Promise.try(fn, ...argumentlar) — fn ni chaqiradi va natijasini doim Promise'ga aylantiradi:

  • fn qiymat qaytarsa — o'sha qiymat bilan bajarilgan Promise;
  • fn Promise qaytarsa — o'sha Promise;
  • fn sinxron xato tashlasa — o'sha xato bilan rad etilgan Promise.
js
function menyuniOqi(matn) {
  const menyu = JSON.parse(matn);
  return Promise.resolve(menyu);
}

Promise.try(menyuniOqi, "{buzuq")
  .then((menyu) => console.log("Menyu:", menyu))
  .catch((xato) => console.log("catch ushladi:", xato.name));

Promise.try(menyuniOqi, '{"osh":35000}')
  .then((menyu) => console.log("Menyu:", menyu));

Konsolda:

text
catch ushladi: SyntaxError
Menyu: { osh: 35000 }

Endi bitta .catch ikkala turdagi xatoni ham ushlaydi. Promise.try ning ikkinchi va keyingi argumentlari fn ga uzatiladi.

6.3 fn qachon ishlaydi?

Muhim farq: Promise.try fn ni darhol, sinxron chaqiradi. Shunga o'xshash eski usul — Promise.resolve().then(fn) — esa fn ni microtask'ga qoldiradi. Tartibni solishtiring:

js
console.log("1. boshlandi");
Promise.resolve().then(() => console.log("4. then ichida"));
Promise.try(() => console.log("2. Promise.try ichida"));
console.log("3. tugadi");

Konsolda:

text
1. boshlandi
2. Promise.try ichida
3. tugadi
4. then ichida

Qadamma-qadam qarang — then callback'i microtask navbatida kutadi, Promise.try ning funksiyasi esa navbatsiz, shu zahoti ishlaydi:

Nega bu muhim? Funksiya tez ishlashi kerak bo'lsa (masalan, foydalanuvchi bosishiga javob), uni bekorga keyinga surish kerak emas. Promise.try — "hozir chaqir, lekin natijani va xatoni Promise qilib ber".

6.4 async funksiya bilan solishtirish

async funksiya ham ichidagi throw ni rad etilgan Promise'ga aylantiradi. Shuning uchun Promise.try(fn) ni shunday ham yozish mumkin edi:

js
const natija = (async () => fn())();

Bu — darhol chaqiriladigan async arrow funksiya. Ishlaydi, lekin o'qish qiyin. Promise.try xuddi shu ishni nomi bilan aytadi.

Promise.try ES2025 da qo'shilgan — eng yangi yordamchi. Baseline ma'lumotiga ko'ra 2025-yil yanvaridan hamma asosiy brauzerda bor (Chrome 128, Firefox 134, Safari 18.2), lekin hali "keng tarqalgan" emas. Node'da 24-versiyada bor, 22-versiyada esa yo'q — Node 22 da typeof Promise.try "undefined" beradi. Eski brauzer yoki Node'ni qo'llab-quvvatlash kerak bo'lsa — async arrow varianti.

Tekshirib ko'ring: Promise.resolve(menyuniOqi("{buzuq")) yozilsa, xato .catch ga tushadimi?

Javob

Yo'q. Avval argument hisoblanadi: menyuniOqi("{buzuq") chaqiriladi va sinxron SyntaxError tashlaydi — Promise.resolve umuman chaqirilmaydi. Promise.try(menyuniOqi, "{buzuq") da esa funksiyani Promise.try ning o'zi chaqiradi va xatoni ushlab, Promise'ga aylantiradi.

7. Ko'p uchraydigan xatolar

7.1 Tayyor Promise'ni new Promise ga o'rash

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function narxniOl() {
  return new Promise((resolve) => {
    kut(100).then(() => resolve(35000));
  });
}

narxniOl().then((narx) => console.log(narx)); // 35000

Ishlaydi, lekin ortiqcha. kut(100) allaqachon Promise — uni yana new Promise ga o'rashning hojati yo'q. Bu naqsh explicit construction anti-pattern ("ortiqcha konstruktor") deb ataladi. Uning xavfi: ichki Promise rad etilsa, tashqi Promise hech qachon hal bo'lmaydi — reject ga hech kim ulanmagan. To'g'risi — Promise'ni qaytarish yoki zanjir qilish:

js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function narxniOl() {
  return kut(100).then(() => 35000);
}

narxniOl().then((narx) => console.log(narx)); // 35000

Qoida: new Promise faqat callback'li yoki hodisali API'ni o'rash uchun. Promise allaqachon bor bo'lsa — then, await yoki return.

7.2 kut ni await siz chaqirish

kut(1000); — hech narsani kutmaydi. U Promise qaytaradi va keyingi qator darhol bajariladi. Kutish faqat await kut(1000) yoki kut(1000).then(...) bilan bo'ladi. Bu async / await darsidagi "suzib yurgan Promise" xatosining o'zi.

7.3 Metodni bind siz promisify qilish

promisify(obyekt.metod) — this yo'qoladi va Cannot read properties of undefined chiqadi. Tuzatish: promisify(obyekt.metod.bind(obyekt)). Undan ham yaxshisi — kutubxonaning tayyor Promise API'sini qidiring.

7.4 Yangi metodni tekshirmasdan ishlatish

Promise.try Node 22 da yo'q: TypeError: Promise.try is not a function — "Promise.try funksiya emas". Xabar g'alati tuyuladi: aslida xususiyat undefined, uni chaqirib bo'lmaydi. Yangi imkoniyatni ishlatishdan oldin loyihangiz qaysi Node va qaysi brauzerlarda ishlashini tekshiring. Bu haqda JS versiyalari va brauzer mosligi darsida batafsil.

8. Mashqlar

1-mashq (oson): Bosqichli tayyorlash

kut(ms) funksiyasini yozing va u bilan choyDamla() async funksiyasini tuzing. U "Suv qaynayapti" deb yozsin, 300 ms kutsin, "Choy solindi" deb yozsin, 200 ms kutsin va "Choy tayyor" deb yozsin. Boshida va oxirida console.log bilan funksiyadan tashqarida "Mehmon kutyapti" deb ham yozing — u qaysi o'rinda chiqishini oldindan ayting.

Yechim
js
function kut(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function choyDamla() {
  console.log("Suv qaynayapti");
  await kut(300);
  console.log("Choy solindi");
  await kut(200);
  console.log("Choy tayyor");
}

choyDamla();
console.log("Mehmon kutyapti");

Konsolda:

text
Suv qaynayapti
Mehmon kutyapti
Choy solindi
Choy tayyor

choyDamla birinchi await gacha sinxron ishlaydi — "Suv qaynayapti" birinchi. await kut(300) da funksiya to'xtaydi va boshqaruv chaqiruvchiga qaytadi — "Mehmon kutyapti". Qolgani taymerlardan keyin.

2-mashq (o'rta): Eski bronni promisify qiling

Callbacklar darsining 1-mashqidagi stolniBronQil(odam, callback) error-first funksiyasini o'zgartirmasdan oling. promisifyQil bilan o'rang va Promise.allSettled (Promise kombinatorlari) bilan uchta bronni parallel bajaring: 3, 0 va 25 kishilik. Har natijani " ..." yoki " ..." deb chiqaring.

Yechim
js
function stolniBronQil(odam, callback) {
  setTimeout(() => {
    if (odam < 1 || odam > 20) {
      callback(new Error("Odam soni 1–20 oralig'ida bo'lsin"));
      return;
    }
    callback(null, `${odam} kishilik stol bron qilindi`);
  }, 200);
}

function promisifyQil(fn) {
  return (...argumentlar) => new Promise((resolve, reject) => {
    fn(...argumentlar, (xato, natija) => {
      if (xato) {
        reject(xato);
        return;
      }
      resolve(natija);
    });
  });
}

const bronQilP = promisifyQil(stolniBronQil);

async function bronlar() {
  const natijalar = await Promise.allSettled(
    [3, 0, 25].map((odam) => bronQilP(odam)),
  );
  for (const n of natijalar) {
    if (n.status === "fulfilled") {
      console.log("✅", n.value);
    } else {
      console.log("❌", n.reason.message);
    }
  }
}

bronlar();

Konsolda:

text
✅ 3 kishilik stol bron qilindi
❌ Odam soni 1–20 oralig'ida bo'lsin
❌ Odam soni 1–20 oralig'ida bo'lsin

Eski funksiyaga bir harf ham tegmadik — promisifyQil uni tashqaridan o'radi. map uchta Promise yasaydi va ular parallel ishlaydi: umumiy vaqt — taxminan 200 ms. promisifyQil bu safar qisqaroq yozilgan: arrow funksiya new Promise(...) ni to'g'ridan-to'g'ri qaytaradi.

3-mashq (qiyin): Hodisani muddat bilan kutish

hodisaniKut(manba, nom, ms) funksiyasini yozing. U Promise qaytarsin:

  • manba (EventTarget) da nom hodisasi sodir bo'lsa — hodisa obyekti bilan bajarilsin;
  • ms ichida sodir bo'lmasa — `${nom} hodisasi ${ms} ms da kelmadi` xatosi bilan rad etilsin;
  • ikkala holatda ham "chiqindi" qolmasin: tinglovchi olib tashlansin, taymer tozalansin.

Promise.withResolvers ishlating. EventTarget, Event va { once: true } — 09-qismdan (Custom events va EventTarget, Listener opsiyalari); Node 24 da ular global. Faylni kurs/mashqlar/11/10-yordamchilar/hodisaniKut.mjs ga saqlang.

Yechim
js
function hodisaniKut(manba, nom, ms) {
  const { promise, resolve, reject } = Promise.withResolvers();

  function tinglovchi(event) {
    clearTimeout(taymer);
    resolve(event);
  }

  const taymer = setTimeout(() => {
    manba.removeEventListener(nom, tinglovchi);
    reject(new Error(`${nom} hodisasi ${ms} ms da kelmadi`));
  }, ms);

  manba.addEventListener(nom, tinglovchi, { once: true });
  return promise;
}

const oshxona = new EventTarget();
setTimeout(() => oshxona.dispatchEvent(new Event("osh-tayyor")), 100);

async function kuzat() {
  const event = await hodisaniKut(oshxona, "osh-tayyor", 300);
  console.log("Keldi:", event.type);
  try {
    await hodisaniKut(oshxona, "manti-tayyor", 200);
  } catch (xato) {
    console.log("Xato:", xato.message);
  }
}

kuzat();

Konsolda:

text
Keldi: osh-tayyor
Xato: manti-tayyor hodisasi 200 ms da kelmadi

Kalit joylar:

  • Hodisa keldi → { once: true } tinglovchini o'zi olib tashlaydi, biz taymerni tozalaymiz.
  • Muddat tugadi → tinglovchini biz olib tashlaymiz. Aks holda u kech kelgan hodisada resolve ni chaqirardi — zarari yo'q, lekin xotirada qolardi.
  • tinglovchi ichida taymer ishlatilgan, garchi u pastroqda e'lon qilingan bo'lsa ham. Bu xato emas: tinglovchi faqat hodisa kelganda chaqiriladi, o'shanda taymer allaqachon tayyor (TDZ).

Node'da bu funksiyaning tayyor varianti bor: events.once(manba, nom) (node:events modulidan). Muddatni esa AbortSignal.timeout bilan berish mumkin — Bekor qilish: AbortController darsida.

bash
git add 11/10-yordamchilar/hodisaniKut.mjs
git commit -m "11/10: hodisaniKut — withResolvers va muddat"

9. Real ishda

  • Node migratsiyasi. Eski Node loyihalarida callback'li kod ko'p. Ularni bosqichma-bosqich async/await ga o'tkazishda util.promisify birinchi qadam bo'ladi. Yangi kodda — node:fs/promises kabi tayyor variantlar.
  • Testlar. "Shu hodisa bo'lguncha kut", "100 ms kut va tekshir" — test kodida kut(ms) va withResolvers doimiy mehmon. Testlarni 12-qismda (Birinchi avtomatik test) yozishni boshlaymiz.
  • Kutubxona ichida. WebSocket klientlari, navbat kutubxonalari, "debounce" qiladigan funksiyalar — ichida withResolvers yoki deferred naqsh bor.
  • Kod sharhi. "Bu new Promise nega kerak? Ichida allaqachon Promise bor-ku" — explicit construction anti-pattern'ni topish sharhda tez-tez uchraydi.
  • Intervyu. "promisify ni o'zingiz yozing" — Promise.all ni yozishdan keyin eng ko'p so'raladigan amaliy savol.

Xulosa

  • Promise.resolve(x) — tayyor qiymatdan (yoki thenable'dan) Promise; Promise bersangiz — o'shaning o'zi. Promise.reject(xato) — tayyor rad etilgan Promise.
  • kut(ms) — Promise + setTimeout; Node'da node:timers/promises.
  • Promisify: callback'ning xato yo'li → reject, natija yo'li → resolve. Node'da util.promisify; metod uchun bind; faqat bir martalik natijalar uchun.
  • Promise.withResolvers() — { promise, resolve, reject }: Promise'ni bir joyda yaratib, boshqa joyda hal qilish.
  • Promise.try(fn) — fn ni darhol chaqiradi; sinxron xato ham rad etilgan Promise bo'ladi. Node 24 da bor, Node 22 da yo'q.
  • new Promise ni faqat callback'li API uchun ishlating — tayyor Promise'ni qayta o'ramang.

Keyingi dars: Microtask va macrotask chuqur — then va await aynan qaysi qadamda microtask yaratadi, murakkab tartib masalalari va microtask'lar taymerlarni qanday "och qoldiradi".

Manbalar

  • ECMAScript spetsifikatsiyasi: "Promise.resolve", "Promise.reject", "Promise.withResolvers", "Promise.try" — tc39.es/ecma262
  • TC39 takliflari: "Promise.withResolvers", "Promise.try" — github.com/tc39
  • Node.js hujjatlari: "util.promisify", "Timers Promises API", "fs/promises" — nodejs.org/api
  • MDN: "Promise.withResolvers()", "Promise.try()" — developer.mozilla.org
  • web-features (Baseline): promise-withresolvers, promise-try — github.com/web-platform-dx/web-features
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
Promise yordamchilari: Promise.resolve, withResolvers, Promise.try va promisify — IlmHamroh