.then
/.catch
/.finally
ishlovchilari doimo asinxrondir.
Va’da zudlik bilan hal qilingan taqdirda ham, sizning .then
/.catch
/.finally
satrlaringizdagidan quyidagi kod birinchi bo’lib bajariladi.
Buni ko’rsatadigan kod:
let promise = Promise.resolve();
promise.then(() => alert("va'da bajarildi"));
alert("kod tugadi"); // birinchi navbatda ushbu ogohlantirish ko'rsatiladi
Agar siz uni ishlatsangiz, avval kod tugadi
, so’ngra va'da tugadi
ni ko’rasiz.
Bu g’alati, chunki va’da boshidanoq albatta amalga oshiriladi.
Nima uchun .then
keyin bajariladi? Nima bo’lyapti?
Mikrovazifalar
Asinxron vazifalar to’g’ri boshqaruvga muhtoj. Buning uchun standart PromiseJobs
ichki navbatini belgilaydi, ko’pincha “mikrovazifa navbat” deb nomlanadi (v8 atama).
Spetsifikatsiyada aytilganidek:
- Navbat birinchi bo’lib amalga oshiriladi: birinchi navbatda vazifalar bo’lib bajariladi.
- Vazifani bajarish faqat boshqa hech narsa ishlamay qolganda boshlanadi.
Yoki sodda qilib aytganda, va’da tayyor bo’lganda, uning .then/catch/finally
ishlovchilari navbatga qo’yiladi. Ular hali bajarilmagan. JavaScript interpretatori navbatdan vazifani oladi va joriy koddan qutilganda uni bajaradi.
Shuning uchun yuqoridagi misolda “kod tugadi” birinchi bo’lib ko’rsatilgan.
Va’da beruvchilar doimo ushbu ichki navbatdan o’tadilar.
Agar bir nechta .then/catch/finally
zanjiri mavjud bo’lsa, unda ularning har biri asinxron tarzda bajariladi. Ya’ni, u avval navbatga qo’yiladi va joriy kod tugagandan va avval navbatda turgan ishlovchilar tugagandan so’ng bajariladi.
Agar navbat biz uchun muhim bo’lsachi? Qanday qilib kod tugadi
ni va'da bajarilgandan
keyin ishlashimiz mumkin?
Oson, uni .then
bilan navbatga qo’ying.
Promise.resolve()
.then(() => alert("va'da bajarilgandan!"))
.then(() => alert("kod tugadi"));
Endi navbat maqsadga muvofiq.
Hodisa tsikli
Brauzer ichidagi JavaScript, shuningdek Node.js hodisa tsikliga asoslangan.
“Hodisa tsikli” – bu interpretator uxlab yotgan va hodisalarni kutib turadgan, keyin ularga javob beradigan va yana uxlaydigan jarayon.
Hodisalar misollari:
mousemove
, foydalanuvchi sichqonchani harakatga keltirdi.setTimeout
ishlov beruvchi chaqirildi.- tashqi
<script src ="...">
yuklangan, bajarishga tayyor. - tarmoq operatsiyasi, masalan.
fetch
tugallandi. - …va h.k.
Hodisalar sodir bo’ladi – interpretator ularni boshqaradi – va yana ko’p narsalar bo’lishini kutadi (uxlash paytida va nolga yaqin CPU iste’mol qilganda).
Ko’rib turganingizdek, bu yerda ham navbat bor. “Makrovazifa navbat” deb nomlangan (v8 atama).
Hodisa sodir bo’lganda, interpretator band bo’lganda, uni boshqarish qulay bo’ladi.
Masalan, interpretator fetch
tarmog’ini qayta ishlash bilan band bo’lganida, foydalanuvchi sichqonchani siljitib, mousemove
keltirib chiqarishi mumkin, va setTimeout
xuddi yuqoridagi rasmda tasvirlanganidek bo’lishi kerak.
Makrovazifa navbatidagi hodisalar “birinchi kelish – birinchi xizmat” tamoyili asosida qayta ishlanadi. Interpretator brauzeri fetch
tugagandan so’ng, u mousemove
hodisasini, keyin setTimeout
ishlov beruvchisini va boshqalarni boshqaradi.
Hozircha juda sodda, to’g’rimi? Interpretator band, shuning uchun boshqa vazifalar navbatda turadi.
Endi muhim narsalar.
Mikrovazifa navbatida makrovazifa navbatiga qaraganda ustunlik yuqori.
Boshqacha qilib aytganda, interpretator avval barcha mikrovazifalarni bajaradi, so’ngra makrovazifalarni oladi. Va’da bilan ishlash har doim birinchi o’ringa ega.
Masalan, qarang:
setTimeout(() => alert("timeout"));
Promise.resolve().then(() => alert("promise"));
alert("code");
Navbat qanday?
code
birinchi navbatda ko’rsatiladi, chunki bu muntazam sinxron chaqiruv.promise
ikkinchi bo’lib ko’rsatiladi, chunki.then
mikrovazifa navbatidan o’tadi va joriy koddan keyin ishlaydi.timeout
oxirgi o’rinda ko’rsatiladi, chunki bu makrovazifa.
Ehtimol, makrovazifada ishlash paytida yangi va’dalar paydo bo’lishi mumkin.
Yoki, aksincha, mikrovazifa makrovazifani rejalashtiradi (masalan, setTimeout
).
Masalan, .then
setTimeout
ni rejalashtiradi:
Promise.resolve()
.then(() => {
setTimeout(() => alert("timeout"), 0);
})
.then(() => {
alert("promise");
});
Tabiiyki, avval promise
paydo bo’ladi, chunki setTimeout
makrovazifani unchalik ahamiyatga ega bo’lmagan makrovazifa navbatida kutmoqda.
Mantiqiy natija sifatida makrovazifalar faqat interpretatorga “bo’sh vaqt” berganda amalga oshiriladi. Shunday qilib, agar bizda hech narsa kutmaydigan va’da zanjiri bo’lsa, unda setTimeout
yoki hodisa ishlovchilari kabi narsalar hech qachon o’rtaga kira olmaydi.
Ishlov berilmagan rad etish
Va'dalar bilan ishlashda xato bobidagi “ishlov berilmagan rad etish” hodisasini eslaysizmi?
Endi mikrovazifalarni tushunish bilan biz uni rasmiylashtira olamiz.
“Ishlov berilmagan rad etish” – bu mikrovazifa navbatining oxirida va’da xatosi ko’rib chiqilmaganda sodir bo’ladi.
Masalan, ushbu kodni ko’rib chiqing:
let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));
// doesn't run: error handled
window.addEventListener('unhandledrejection', event => alert(event.reason));
Biz rad etilgan promise
ni yaratamiz va xatoni ko’rib chiqmaymiz. Shunday qilib bizda “ishlov berilmagan rad etish” hodisasi mavjud (brauzer konsolida ham yozilgan).
Agar .catch
qo’shsak, bizda bunday bo’lmaydi:
let promise = Promise.reject(new Error("Promise Failed!"));
// hech qanday xato yo'q, hammasi tinch
window.addEventListener("unhandledrejection", (event) => alert(event.reason));
Endi aytaylik, biz xatoga yo’l qo’yamiz, ammo etTimeout
dan keyin:
let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')), 1000);
// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));
Endi ishlov berilmagan rad etish yana paydo bo’ladi. Nima uchun? Mikrovazifada navbat tugagandan so’ng, “ishlov berilmagan rad etish” paydo bo’ladi. Interpretator va’dalarni tekshiradi va agar ulardan biri “rad etilgan” holatda bo’lsa, unda hodisa sodir bo’ladi.
Masalan, setTimeout
tomonidan qo’shilgan .catch
ham ishga tushadi, albatta, lekin unhandledrejection
allaqachon sodir bo’lgandan so’ng.
Xulosa
-
Va’dalarni boshqarish har doim ham mos kelmaydi, chunki barcha va’da qilingan harakatlar “mikrovazifa navbati” (v8 atama) deb nomlangan ichki “va’da ishi” navbatidan o’tadi.
Shunday qilib,
.then/catch/finally
joriy kod tugagandan so’ng chaqiriladi.Agar kodning bir qismi
.then/catch/finally
dan keyin bajarilishini kafolatlashimiz kerak bo’lsa, uni zanjirlangan.then
chaqiruviga qo’shganimiz ma’qul. -
Shuningdek, turli xil hodisalarni, tarmoq ish natijalarini,
"setTimeout
rejalashtirilgan chaqiruvlarni va boshqalarni saqlaydigan “makrovazifa navbati” mavjud. Ular “makrovazifalar” (v8 atama) deb ham ataladi.Interpretator ularni tashqi ko’rinish tartibida boshqarish uchun makrovazifalrdan foydalanadi.
Makrovazifalar kod tugagandan so’ng ishlaydi va mikrovazifalar navbati bo’sh bo’lgandan keyin.
Boshqacha qilib aytganda, ularning ustunligi pastroq.
Shunday qilib, navbat quyidagicha: odatdagi kod, keyin va’da bilan ishlash, so’ngra hamma narsa, masalan hodisalar va boshqalar.
Izohlar
<code>
yorlig'ini ishlating, bir nechta satrlar uchun - ularni<pre>
yorlig'i bilan o'rab qo'ying, 10 satrdan ortiq bo'lsa - sandbox (plnkr, jsbin, codepen…)