javascript - Ce se întâmplă cu acest lanț de Promisiuni?
Îmi scarpin capul încercând să înțeleg ce se întâmplă cu următoarele cazuri.
Cazul 1:
async function doSomething(): Promise<void> {
return new Promise(resolve => {
const promise = (async () => "A")();
promise
.then(() => {
console.log("THEN");
resolve();
})
.catch(() => {
console.log("CATCH");
})
.finally(() => {
console.log("FINALLY");
});
});
}
async function main() {
await doSomething();
console.log("DONE");
}
main().catch(console.error);
Ieșire, după cum era de așteptat:
THEN
FINALLY
DONE
Dar în cazul 2:
async function doSomething(): Promise<void> {
return new Promise(resolve => {
const promise = (async () => Promise.resolve("A"))();
promise
.then(() => {
console.log("THEN");
resolve();
})
.catch(() => {
console.log("CATCH");
})
.finally(() => {
console.log("FINALLY");
});
});
}
async function main() {
await doSomething();
console.log("DONE");
}
main().catch(console.error);
Rezultatul este:
THEN
DONE
FINALLY
Și cazul 3:
async function doSomething(): Promise<void> {
return new Promise(resolve => {
const promise = (async () => Promise.resolve("A"))();
promise
.then(() => {
console.log("THEN");
resolve();
})
.finally(() => {
console.log("FINALLY");
});
});
}
async function main() {
await doSomething();
console.log("DONE");
}
main().catch(console.error);
Rezultatul este:
THEN
FINALLY
DONE
Ce se întâmplă exact cu lanțul de promisiuni?
De ce cazul 2 nu afișează „ATUNCI, ÎN FINAL, TERMINAT”?
De ce cazul 3 se comportă diferit față de cazul 2?
Răspuns #1
S-ar putea să fi înțeles ce s-a întâmplat cu cele 3 cazuri.
Încărcare rapidă
Diferența în numărul de promisiuni și rezolvarea promisiunilor cu promisiuni schimbă ordinea microtask-urilor puse în coadă, cauzând diferența în declarațiile de tipărire.
Simplificarea codului
În primul rând, să încercăm să simplificăm codul eliminând async/await. Voi folosi codul din cazul 1 pentru simplificare.
Blocul principal
async function doSomething(): Promise<void> {
return new Promise(resolve => {
...
});
}
async function main() {
await doSomething();
console.log("DONE");
}
main().catch(console.error);
poate fi rescris astfel:
function doSomething(): Promise<void> {
// Promise A
return new Promise(resolve => {
// Promise B
const b = new Promise(resolve => {
...
});
resolve(b);
});
}
doSomething()
.then(function externalThen() {
console.log("DONE");
});
Declarația promise pentru cazul 1:
const promise = (async () => "A")();
devine :
const promise = Promise.resolve("A");
și pentru cazurile 2 și 3:
const promise = (async () => Promise.resolve("A"))();
devine :
const promise = new Promise(resolve => {
resolve(Promise.resolve("A"))
});
Punând totul împreună și eliminând funcțiile și declarațiile de variabile inutile:
// Promise A
new Promise(resolve => {
// Promise B
const b = new Promise<void>(resolve => {
// Promise C
new Promise(resolve => {
resolve("A");
})
.then(function then1() {
console.log("THEN");
resolve();
})
.catch(function catch1() {
console.log("CATCH");
})
.finally(function finally1() {
console.log("FINALLY");
});
});
resolve(b);
})
.then(function externalThen() {
console.log("DONE");
});
Pentru cazurile 2 și 3, declarația Promise C devine:
// Promise C
new Promise(resolve => {
// Promise D
resolve(Promise.resolve("A"));
})
Cum este executat codul Promise
- funcția executor transmisă constructorului Promise este executată imediat.
- callback-urile transmise metodelor
then,catchșifinallysunt executate ca microtask atunci când promisiunea se îndeplinește. - microtask este adăugat în o coadă de microtask
- coada de micro-sarcini este golită și toate sarcinile sunt executate, în ordinea FIFO, când stiva JS este goală
- trecerea unui Promise
p1la callback-ulresolveal unui Promisep2generează o nouă microactivitate care leagăp1dep2. Metodathenap1este apelată trecând callback-urileresolveșirejectalep2.
Caz de executare 1
// Promise A
new Promise(resolve => {
// Promise B
const b = new Promise<void>(resolve => {
// Promise C
new Promise(resolve => {
resolve("A");
})
.then(function then1() {
console.log("THEN");
resolve();
})
.catch(function catch1() {
console.log("CATCH");
})
.finally(function finally1() {
console.log("FINALLY");
});
});
resolve(b);
})
.then(function externalThen() {
console.log("DONE");
});
- executarea parcurge constructorii Promise pentru Promise A, B și C
- C este rezolvat imediat
- executarea callback-ului
then1este adăugată în coada microtask-urilor - Execuția continuă să lanțeze promisiunile până
la rezolvarea (b) beste o Promisiune care este transmisă ca valoare de rezolvare pentru Promisiunea A. Acest lucru generează o nouă micro-sarcină care leagă B de A.- Sfârșitul blocului principal, două microtask-uri în coadă:
then1,chain-B-to-A. executarea
then1:- afișează în consolă "THEN"
- rezolvă Promise
b - Promise-ul generat de
thense rezolvă și pune în coadă o micro-sarcină pentrucatch1
execuția lanțului-B-la-A:
- în pseudo-cod:
b.then(a.resolve, a.reject)
then-B-A este pusă în coadăcatch, then-B-Aexecutarea catch1:
- nimic de-a face cu apelul înapoi
finally1executarea lui then-B-A:
- Promisiunea A se rezolvă și pune în coadă o micro-sarcină pentru
externalThen
finally1, externalThenfinally1: afișează "FINALLY"externalThen: afișează "GATA"Caz de executare 2
// Promise A
new Promise(resolve => {
// Promise B
const b = new Promise<void>(resolve => {
// Promise C
new Promise(resolve => {
// Promise D
resolve(Promise.resolve("A"));
})
.then(function then1() {
console.log("THEN");
resolve();
})
.catch(function catch1() {
console.log("CATCH");
})
.finally(function finally1() {
console.log("FINALLY");
});
});
resolve(b);
})
.then(function externalThen() {
console.log("DONE");
});
executarea parcurge constructorii Promise pentru Promise A, B și C
Promisiunea C este rezolvată cu Promisiunea D, aceasta generează o micro-sarcină pentru a lega D de C
Execuția continuă să lanțeze promisiunile până
la rezolvarea (b)beste o Promisiune care este transmisă ca valoare de rezolvare pentru Promisiunea A. Acest lucru generează o nouă micro-sarcină care leagă B de A.Sfârșitul blocului principal, două microtaskuri în coadă:
chain-D-to-C,chain-B-to-A.executarea
chain-D-to-C:- în pseudo-cod:
d.then(c.resolve, c.reject)
- în pseudo-cod:
- micro-sarcina
then1este pusă în coadă
execuția lanțului-B-la-A:
- în pseudo-cod:
b.then(a.resolve, a.reject)
coada microtask în acest moment: then1
executarea then1:
- afișează în consolă "THEN"
b, aceasta pune în coadă microtask-ul then-B-Athen se rezolvă și pune în coadă o micro-sarcină pentru catch1coada microtask în acest moment: then-B-A, catch1
executarea lui then-B-A:
- Promisiunea A se rezolvă și pune în coadă o micro-sarcină pentru
externalThen
executarea catch1:
- nimic de-a face cu apelul înapoi
finally1coada microtask-urilor în acest moment: externalThen, finally1
executarea externalThen: afișează "GATA"
executarea finally1: afișează "FINALLY"
fără microtask-uri, codul se termină
Caz de executare 3
// Promise A
new Promise(resolve => {
// Promise B
const b = new Promise<void>(resolve => {
// Promise C
new Promise(resolve => {
// Promise D
resolve(Promise.resolve("A"));
})
.then(function then1() {
console.log("THEN");
resolve();
})
.finally(function finally1() {
console.log("FINALLY");
});
});
resolve(b);
})
.then(function externalThen() {
console.log("DONE");
});
Tot identic cu cazul 2 până aici:
- coada microtask în acest moment:
then1 executarea
then1:- afișează în consolă "APOI"
- rezolvă Promise
b, aceasta pune în coadă microtask-ulthen-B-A - Promisiunea generată de
thense rezolvă și pune în coadă o micro-sarcină pentrufinally1
then-B-A, finally1executarea lui then-B-A:
- Promise A se rezolvă și adaugă în coadă o micro-sarcină pentru
externalThen
finally1: afișează "FINALLY"externalThenexternalThen: afișează "GATA"