PÅÃslibové handlery .then/.catch/.finally jsou vždy asynchronnÃ.
I když je pÅÃslib okamžitÄ splnÄn, kód na ÅádcÃch pod .then/.catch/.finally se vykoná jeÅ¡tÄ pÅed tÄmito handlery.
Zde je ukázka:
let pÅÃslib = Promise.resolve();
pÅÃslib.then(() => alert("pÅÃslib hotov!"));
alert("konec kódu"); // tento alert se zobrazà jako prvnÃ
Pokud si ji spustÃte, uvidÃte napÅed konec kódu a až poté pÅÃslib hotov!.
To je zvláštnÃ, protože pÅÃslib je oÄividnÄ hotov již od zaÄátku.
ProÄ se .then spustà až pak? Co se dÄje?
Fronta mikroúloh
Asynchronnà úlohy potÅebujà vhodný management. Proto standard ECMA specifikuje vnitÅnà frontu PromiseJobs, ÄastÄji nazývanou âfronta mikroúlohâ (pojem z V8).
Jak je uvedeno ve specifikaci:
- Fronta funguje na principu âprvnà dovnitÅ, prvnà venâ: úlohy vložené jako prvnà se spustà jako prvnÃ.
- ProvádÄnà úlohy se spustà jen tehdy, když nebÄžà nic jiného.
Nebo, abychom to zjednoduÅ¡ili, když je pÅÃslib pÅipraven, vložà se do fronty jeho handlery .then/catch/finally; zatÃm nebudou spuÅ¡tÄny. Až se motor JavaScriptu oprostà od právÄ provádÄného kódu, vybere úlohu z fronty a spustà ji.
To je důvod, proÄ se v uvedeném pÅÃkladu jako prvnà zobrazà âkonec kóduâ.
PÅÃslibové handlery touto vnitÅnà frontou vždy procházejÃ.
Jestliže máme ÅetÄz s vÃce .then/catch/finally, je každý z nich spuÅ¡tÄn asynchronnÄ. To znamená, že nejprve je vložen do fronty a spustà se až tehdy, když bude dokonÄen právÄ provádÄný kód i handlery ve frontÄ pÅed nÃm.
Co když nám záležà na poÅadÃ? Jak můžeme zajistit, aby se konec kódu objevil až po pÅÃslib hotov!?
JednoduÅ¡e, prostÄ jej vložÃme do fronty pomocà .then:
Promise.resolve()
.then(() => alert("pÅÃslib hotov!"))
.then(() => alert("konec kódu"));
Nynà je poÅadà takové, jaké jsme zamýšleli.
NeoÅ¡etÅené odmÃtnutÃ
Pamatujete si na událost unhandledrejection z Älánku OÅ¡etÅovánà chyb pomocà pÅÃslibů?
Nynà pÅesnÄ vidÃme, jak JavaScript zjistÃ, že nastalo neoÅ¡etÅené odmÃtnutÃ.
âNeoÅ¡etÅené odmÃtnutÃâ nastane, když na konci fronty mikroúloh nenà oÅ¡etÅena pÅÃslibová chyba.
Za normálnÃch okolnostÃ, pokud oÄekáváme chybu, pÅidáme do ÅetÄzu pÅÃslibů .catch, aby ji oÅ¡etÅil:
let pÅÃslib = Promise.reject(new Error("PÅÃslib selhal!"));
pÅÃslib.catch(chyba => alert('odchyceno'));
// nespustà se: chyba je oÅ¡etÅena
window.addEventListener('unhandledrejection', událost => alert(událost.reason));
Jestliže vÅ¡ak zapomeneme pÅidat .catch, pak motor po vyprázdnÄnà fronty mikroúloh tuto událost spustÃ:
let pÅÃslib = Promise.reject(new Error("PÅÃslib selhal!"));
// PÅÃslib selhal!
window.addEventListener('unhandledrejection', událost => alert(událost.reason));
Co když tuto chybu oÅ¡etÅÃme pozdÄji? NapÅÃklad:
let pÅÃslib = Promise.reject(new Error("PÅÃslib selhal!"));
setTimeout(() => pÅÃslib.catch(chyba => alert('odchyceno')), 1000);
// PÅÃslib selhal!
window.addEventListener('unhandledrejection', událost => alert(událost.reason));
Když to nynà spustÃme, uvidÃme nejprve PÅÃslib selhal! a pak odchyceno.
Kdybychom nevÄdÄli o frontÄ mikroúloh, mohli bychom se divit: âProÄ se handler unhandledrejection spustil? VždyÅ¥ jsme tu chybu zachytili a oÅ¡etÅili!â
Nynà vÅ¡ak rozumÃme, že unhandledrejection se generuje, když je fronta mikroúloh dokonÄena: motor prozkoumá pÅÃsliby, a pokud nÄkterý z nich je ve stavu zamÃtnutÃ, událost se spustÃ.
V uvedeném pÅÃkladu se spustà i .catch, který pÅidala funkce setTimeout. Stane se to vÅ¡ak pozdÄji, až poté, co již nastala událost unhandledrejection, takže se tÃm nic nezmÄnÃ.
ShrnutÃ
Zpracovánà pÅÃslibů je vždy asynchronnÃ, jelikož vÅ¡echny pÅÃslibové akce procházejà vnitÅnà frontou âpÅÃslibových úkolůâ, nazývanou také âfronta mikroúlohâ (pojem z V8).
Proto jsou handlery .then/catch/finally volány vždy až poté, co skonÄà aktuálnÄ provádÄný kód.
Jestliže potÅebujeme zaruÄit, aby se Äást kódu spustila až po .then/catch/finally, můžeme ji pÅidat do zÅetÄzeného volánà .then.
Ve vÄtÅ¡inÄ motorů JavaScriptu, vÄetnÄ prohlÞeÄů a Node.js, je koncept mikroúloh úzce spojen se âsmyÄkou událostÃâ a âmakroúlohamiâ. Protože tyto záležitosti pÅÃmo nesouvisejà s pÅÃsliby, probereme je v jiné Äásti tutoriálu v Älánku SmyÄka událostÃ: mikroúlohy a makroúlohy.
KomentáÅe
<code>, pro nÄkolik Åádků je obalte znaÄkou<pre>, pro vÃce než 10 Åádků vložte odkaz na pÃskoviÅ¡tÄ (plnkr, jsbin, codepenâ¦)