TL; DR: Porque +=
lê x
antes, mas grava após alterar, devido à await
palavra - chave em seu segundo operando (lado direito).
async
As funções são executadas de forma síncrona quando foram chamadas até a primeira await
instrução.
Portanto, se você remover await
, ele se comportará como uma função normal (com a exceção de que ainda retorna uma promessa).
Nesse caso, você obtém 5
e 6
no console:
let x = 0;
async function test() {
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
A primeira await
interrompe a execução síncrona, mesmo que seu argumento esteja disponível de forma síncrona; portanto, o seguinte retornará 1
e 6
, conforme o esperado:
let x = 0;
async function test() {
// Enter asynchrony
await 0;
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
No entanto, seu caso é um pouco mais complicado.
Você colocou await
dentro de uma expressão que usa +=
.
Você provavelmente sabe que em JS x += y
é idêntico a x = (x + y)
. Usarei o último formulário para entender melhor:
let x = 0;
async function test() {
x = (x + await 5);
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Quando o intérprete chega a esta linha ...
x = (x + await 5);
... começa a avaliar e passa a ...
x = (0 + await 5);
... então, chega ao await
e para.
O código após a chamada da função começa a ser executado, modifica o valor de x
e registra-o.
x
é agora 1
.
Depois que o script principal é encerrado, o intérprete retorna à test
função em pausa e continua avaliando essa linha:
x = (0 + 5);
E, como o valor de x
já está substituído, ele permanece 0
.
Finalmente, o intérprete faz a adição, lojas 5
para x
, e registra-lo.
Você pode verificar esse comportamento efetuando login em um getter / setter de propriedade de objeto (neste exemplo, y.z
reflete o valor de 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)
ex += await 5
.