TL; DR: Ponieważ +=czyta xwcześniej, ale zapisuje go po zmianie, ze względu na awaitsłowo kluczowe w drugim operandzie (po prawej stronie).
asyncfunkcje działają synchronicznie, gdy wywoływane są do pierwszej awaitinstrukcji.
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ć 5i 6w konsoli:
let x = 0;
async function test() {
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Pierwszy awaitprzestaje działać synchronicznie, nawet jeśli jego argument jest dostępny synchronicznie, więc zostaną zwrócone następujące elementy 1i 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ś awaitwyrażenie, które używa +=.
Prawdopodobnie wiesz, że w JS x += yjest 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 awaiti zatrzymuje się.
Kod po wywołaniu funkcji zaczyna działać, modyfikuje wartość x, a następnie rejestruje ją.
xjest teraz 1.
Następnie, po zakończeniu skryptu głównego, interpreter wraca do wstrzymanej testfunkcji i kontynuuje ocenę tej linii:
x = (0 + 5);
A ponieważ wartość xjest już podstawiona, pozostaje 0.
Wreszcie, interpreter robi dodatek, przechowuje 5się xi 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.zodzwierciedla 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.