Promise handlers .then/.catch/.finally er altid asynkrone.
Selv når et promise løses umiddelbart, vil koden på linjerne under .then/.catch/.finally stadig køre før disse handlers.
Her er et eksempel:
let promise = Promise.resolve();
promise.then(() => alert("promise færdig!"));
alert("kode færdig"); // denne kode vises først
Hvis du kører det, ser du kode færdig først, og så promise færdig!.
Det er mærkeligt, fordi promise er helt sikkert færdig fra starten.
Hvorfor aktiverede .then efterfølgende? Hvad sker der?
Microtasks queue
Asynkrone opgaver har brug for korrekt håndtering. Til det formål specificerer ECMA-standarden en intern kø PromiseJobs, som ofte refereres til som “microtask queue” (V8 term).
Som angivet i specifikationen:
- Køen er first-in-first-out: opgaver, der er sat i køen først, køres først.
- Udførelse af en opgave initieres kun, når intet andet er i gang.
Eller, sagt på en simpel måde, når et promise er færdigt, bliver dets .then/catch/finally handlers sat i køen; de bliver ikke udført endnu. Når JavaScript-motoren bliver ledig fra den nuværende kode, tager den en opgave fra køen og udfører den.
Det er derfor “kode færdig” i eksemplet ovenfor vises først.
Promise handlers går altid gennem denne interne kø.
Hvis der er en kæde med flere .then/catch/finally, så udføres hver enkelt asynkront. Det vil sige, at de først sættes i køen, og derefter udføres, når den nuværende kode er færdig og tidligere satte handlers er færdige.
Hvad hvis rækkefølgen er vigtig for os? Hvordan kan vi få kode færdig til at vises efter promise færdig?
Simpelthen ved at putte det i køen med .then:
Promise.resolve()
.then(() => alert("promise færdig!"))
.then(() => alert("kode færdig"));
Nu er rækkefølgen som ønsket.
Uhåndteret afvisning
Husker du unhandledrejection eventen fra artiklen Håndtering af fejl med promises?
Nu kan vi se præcis hvordan JavaScript finder ud af, at der er en uhåndteret afvisning.
En “uhåndteret afvisning” opstår, når en promise fejl ikke håndteres i slutningen af microtask-køen.
Normalt, hvis vi forventer en fejl, tilføjer vi .catch til promise-kæden for at håndtere den: