Гузариш ба мундариҷа
Мавзӯи 18 аз 21
Назария

Промисҳо

Ин барои чӣ лозим аст

Дар мавзӯи гузашта натиҷаи асинхрониро тавассути callback медоданд ва ҳангоми якчанд қадам «зинапоя»-и функсияҳои ба ҳам ҷойгир пайдо мешуд. Промис (Promise — «ваъда») — объектест, ки натиҷаи ояндаро ифода мекунад. Онро бо return-и оддӣ аз функсия баргардондан, дар тағйирёбанда нигоҳ доштан, ба пеш додан ва ба натиҷа обуна шудан мумкин аст.

Тамоми JavaScript-и муосир бар промисҳо сохта шудааст: fetch (дархостҳо ба сервер), хондани файлҳо дар Node.js, async/await, бор кардани додаҳо дар React. Ҳатто агар await нависед, дар зери он ҳамеша промисҳо ҳастанд — барои ҳамин онҳоро фаҳмидан лозим аст.

Се ҳолат

Промис ҳамеша дар яке аз се ҳолат аст:

Ҳолат Маънояш
pending (интизорӣ) натиҷа ҳоло нест
fulfilled (иҷрошуда) натиҷа ҳаст — қимат
rejected (радшуда) хато рух дод — сабаб ҳаст

Аз интизорӣ промис ба яке аз ду ҳолати ниҳоӣ як бор мегузарад ва дигар тағйир намеёбад.

Сохтани промис: new Promise

function delay(ms) {
  return new Promise((resolve) => {
    setTimeout(() => resolve("тайёр"), ms);
  });
}

console.log("Интизорем...");
delay(100).then((result) => console.log(result));

Натиҷа:

Интизорем...
тайёр
  • Ба new Promise(…) функсияи «иҷрокунанда»-ро медиҳанд. Вай фавран оғоз мешавад ва ду функсия мегирад: resolve(қимат) — «бомуваффақият иҷро шуд» ва reject(хато) — «нашуд».
  • .then(callback) — «вақте натиҷа шуд, callback-ро бо он даъват кун».

Мисол бо хато:

function checkAge(age) {
  return new Promise((resolve, reject) => {
    if (age >= 18) resolve("Дастрасӣ кушода шуд");
    else reject(new Error("Ҳоло барвақт аст"));
  });
}

checkAge(20).then((text) => console.log(text));
checkAge(12)
  .then((text) => console.log(text))
  .catch((err) => console.log("Хато: " + err.message));

Натиҷа:

Дастрасӣ кушода шуд
Хато: Ҳоло барвақт аст

.catch(callback) радшавиро медорад. Ба reject одатан объекти Error-ро медиҳанд, мисли throw.

finally

.finally(callback) дар ҳар ҳолат иҷро мешавад — муваффақ ё не. Масалан, барои пинҳон кардани нишондиҳандаи боркунӣ:

checkAge(12)
  .then((text) => console.log(text))
  .catch((err) => console.log("Хато: " + err.message))
  .finally(() => console.log("Боркунӣ тамом шуд"));

Занҷирҳои then

Қуввати асосии промисҳо: then худаш промиси нав бармегардонад. Он чи callback баргардонд — промиси навбатӣ бо ҳамон иҷро мешавад. Ҳамин тавр қадамҳо бе ҷойгиршавӣ паси ҳам меоянд:

Promise.resolve(2)
  .then((x) => x * 10)          // 20
  .then((x) => x + 1)           // 21
  .then((x) => console.log("Ҳосил: " + x));

Натиҷа:

Ҳосил: 21

Агар callback промис баргардонад, занҷир онро интизор мешавад:

const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));

wait(50, "Аня")
  .then((name) => wait(50, name + " аз Душанбе"))   // боз як промисро интизор мешавем
  .then((text) => console.log(text));

Натиҷа:

Аня аз Душанбе

Бо «дӯзахи callback-ҳо» аз мавзӯи гузашта муқоиса кунед: қадамҳо ҳамчун рӯйхати ҳамвор навишта шудаанд.

Хатоҳо дар занҷир

Хато (тавассути reject ё throw-и оддӣ дар дохили then) ҳамаи then-ҳои навбатиро мегузарад ва ба catch-и наздиктарин меафтад. Барои ҳамин як catch дар охир барои тамоми занҷир кифоя аст:

Promise.resolve(1)
  .then((x) => {
    throw new Error("дар қадами 2 шикаст");
  })
  .then((x) => console.log("ин қадам гузаронида мешавад"))
  .catch((err) => console.log("Доштем: " + err.message))
  .then(() => console.log("Баъд аз catch занҷир идома дорад"));

Натиҷа:

Доштем: дар қадами 2 шикаст
Баъд аз catch занҷир идома дорад

catch ҳам промис бармегардонад: он чи вай баргардонд, қимати нав мешавад. Ҳамин тавр қимати пешфарзро мегузоранд: .catch(() => []).

Якчанд промис якбора

Аксар вақт якчанд вазифаро параллелӣ оғоз карда, ҳамаашонро интизор шудан лозим аст. Барои ин усулҳои статикии Promise ҳастанд:

const wait = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms));

const start = Date.now();
Promise.all([wait(100, "A"), wait(100, "B"), wait(100, "C")]).then((values) => {
  console.log(values);
  console.log("Зудтар аз 200 мс: " + (Date.now() - start < 200));
});

Натиҷа:

["A","B","C"]
Зудтар аз 200 мс: true

Се интизорӣ ҳар кадом 100 мс тақрибан 100 мс гирифт, на 300: онҳо якбора мерафтанд.

Усул Кай иҷро мешавад Натиҷа
Promise.all(arr) ҳама иҷро шуданд массиви қиматҳо бо тартиби массив; ҳангоми хатои аввал — рад мешавад
Promise.allSettled(arr) ҳама ба ҳар роҳ тамом шуданд массиви { status, value } ё { status, reason }
Promise.race(arr) аввалин тамом шуд қимат ё хатои он
Promise.any(arr) аввалин муваффақ қимати он

allSettled вақте қулай аст, ки як нокомӣ набояд ҳамаро вайрон кунад:

Promise.allSettled([Promise.resolve(1), Promise.reject(new Error("не")), Promise.resolve(3)]).then((results) => {
  console.log(results.map((r) => (r.status === "fulfilled" ? r.value : "хато")));
});

Натиҷа:

[1,"хато",3]

race бошад — усули гузоштани маҳдудияти вақт: «агар ҷавоб дар 3 сония наояд — хато».

Дар дохил чӣ гуна кор мекунад: микровазифаҳо

Callback-ҳои then, catch ва finally ҳеҷ гоҳ фавран даъват намешаванд, ҳатто агар промис аллакай иҷро шуда бошад. Онҳо ба навбати махсус — микровазифаҳо меафтанд. Давраи рӯйдодҳо онро фавран баъд аз коди ҷорӣ ва пеш аз вазифаҳои оддӣ, мисли таймерҳо, коркард мекунад:

console.log("1: коди синхронӣ");
setTimeout(() => console.log("4: таймер"), 0);
Promise.resolve().then(() => console.log("3: then"));
console.log("2: коди синхронӣ");

Натиҷа:

1: коди синхронӣ
2: коди синхронӣ
3: then
4: таймер

Тартиб чунин аст: тамоми коди синхронӣ → ҳамаи микровазифаҳо (промисҳо) → вазифаи навбатӣ аз навбат (таймер, клик) → боз микровазифаҳо → …

Боз як хосияти муҳим: функсияи иҷрокунанда дар new Promise синхронӣ, фавран ҳангоми сохтан иҷро мешавад. Танҳо аксуламалҳо ба натиҷа асинхронӣ мебошанд.

Хатоҳои маъмул

  1. return-и фаромӯшшуда дар then. .then((x) => { x * 2; }) ба пеш undefined медиҳад. Ё тирчаи кӯтоҳ (x) => x * 2, ё return.
  2. Промис дар дохили then бе return. Занҷир онро интизор намешавад ва хатоҳояшро касе намедорад.
  3. catch нест. Промиси радшуда бе коркардкунанда — «хатои коркарднашуда», дар Node.js вай барномаро қатъ мекунад.
  4. Паси ҳам интизор шудани он чи параллелӣ мумкин аст. Боркуниҳои мустақилро якҷоя оғоз кунед ва бо Promise.all интизор шавед.
  5. Кӯшиши синхронӣ гирифтани қимат. const x = delay(100); — дар x промис аст, на натиҷа. Натиҷа танҳо дар then (ё бо await, мавзӯи навбатӣ) дастрас аст.

Ёддошт

Чӣ Чӣ тавр
Сохтан new Promise((resolve, reject) => { ... })
Промиси тайёр Promise.resolve(x), Promise.reject(err)
Натиҷа .then((value) => ...)
Хато .catch((err) => ...)
Дар ҳар ҳолат .finally(() => ...)
Ҳамаро интизор шудан Promise.all([...])
Ҳама, ҳатто бо хатоҳо Promise.allSettled([...])
Кӣ аввал Promise.race([...])
Тартиб коди синхронӣ → микровазифаҳо (then) → таймерҳо

Таваққуф

Осон+10 XP

Функсияи delay(ms, value)-ро нависед, ки промис бармегардонад. Баъд аз ms миллисония промис бо қимати value иҷро мешавад.

delay(500, "ready").then((v) => console.log(v));   // баъд аз ним сония: ready

Барои ҳал кардани масъалаҳо коннекторро пайваст кун

Коди масъалаҳо дар компютери худат иҷро мешавад — ин аз браузер тезтар ва боэътимодтар аст. Барои ин барномаи ройгони коннектори CodeWithMe лозим аст. Онро як бор пайваст кардан кифоя аст.

Коннекторро дар ин компютер ҷустуҷӯ мекунем…

  1. 1
    Коннекторро барои системаи худ (Windows, macOS ё Linux) зеркашӣ кун.Зеркашии коннекторДар саҳифаи зеркашӣ файлеро интихоб кун, ки дар номаш системаи ту ҳаст, масалан windows-amd64.
  2. 2Онро иҷро кун ва ҷузвдонеро интихоб кун, ки ҳалҳоят дар он ҷо меистанд. Python ва Node.js-ро ӯ худаш ҳангоми зарурат ба он ҷо мегузорад.
  3. 3
    Дар поён рамзи 6-рақамаро ворид кун — он дар равзанаи коннектор навишта шудааст («Рамз барои сайти CodeWithMe»). Агар браузер дар бораи дастрасӣ ба шабакаи маҳаллӣ пурсад — иҷозат деҳ.

Коннектор кушода аст (рамзи манбаъ дар GitHub), танҳо дар компютери ту кор мекунад ва кодро танҳо пас аз ворид кардани рамзи пайвастшавӣ иҷро мекунад.