TL; DR: Ponieważ +=
czyta x
wcześniej, ale zapisuje go po zmianie, ze względu na await
słowo kluczowe w drugim operandzie (po prawej stronie).
async
funkcje działają synchronicznie, gdy wywoływane są do pierwszej await
instrukcji.
Jeśli więc usuniesz await
, zachowuje się jak normalna funkcja (z wyjątkiem tego, że nadal zwraca obietnicę).
W takim przypadku, można uzyskać 5
i 6
w konsoli:
let x = 0;
async function test() {
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Pierwszy await
przestaje działać synchronicznie, nawet jeśli jego argument jest dostępny synchronicznie, więc zostaną zwrócone następujące elementy 1
i 6
, zgodnie z oczekiwaniami:
let x = 0;
async function test() {
// Enter asynchrony
await 0;
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Twoja sprawa jest jednak nieco bardziej skomplikowana.
Wstawiłeś await
wyrażenie, które używa +=
.
Prawdopodobnie wiesz, że w JS x += y
jest identyczny x = (x + y)
. Użyję tego drugiego formularza dla lepszego zrozumienia:
let x = 0;
async function test() {
x = (x + await 5);
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Kiedy tłumacz osiągnie tę linię ...
x = (x + await 5);
... zaczyna to oceniać i przechodzi w ...
x = (0 + await 5);
... wtedy osiąga await
i zatrzymuje się.
Kod po wywołaniu funkcji zaczyna działać, modyfikuje wartość x
, a następnie rejestruje ją.
x
jest teraz 1
.
Następnie, po zakończeniu skryptu głównego, interpreter wraca do wstrzymanej test
funkcji i kontynuuje ocenę tej linii:
x = (0 + 5);
A ponieważ wartość x
jest już podstawiona, pozostaje 0
.
Wreszcie, interpreter robi dodatek, przechowuje 5
się x
i rejestruje go.
Możesz sprawdzić to zachowanie, logując się w obiekcie pobierającym / ustawiającym właściwości obiektu (w tym przykładzie y.z
odzwierciedla wartość x
:
let x = 0;
const y = {
get z() {
console.log('get x :', x);
return x;
},
set z(value) {
console.log('set x =', value);
x = value;
}
};
async function test() {
console.log('inside async function');
y.z += await 5;
console.log('x :', x);
}
test();
console.log('main script');
y.z += 1;
console.log('x :', x);
/* Output:
inside async function
get x : 0 <-- async fn reads
main script
get x : 0
set x = 1
x : 1
set x = 5 <-- async fn writes
x : 5 <-- async fn logs
*/
/* Just to make console fill the available space */
.as-console-wrapper {
max-height: 100% !important;
}
await (x += 5)
ix += await 5
.