TL; DR: Porque +=
lee x
antes, pero lo escribe después de que ha cambiado, debido a la await
palabra clave en su segundo operando (lado derecho).
async
Las funciones se ejecutan sincrónicamente cuando llaman hasta la primera await
instrucción.
Entonces, si elimina await
, se comporta como una función normal (con la excepción de que aún devuelve una Promesa).
En ese caso, obtienes 5
y 6
en la consola:
let x = 0;
async function test() {
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
El primero await
detiene la ejecución síncrona, incluso si su argumento está disponible sincrónicamente, por lo que se devolverá lo siguiente 1
y 6
, como es de esperar:
let x = 0;
async function test() {
// Enter asynchrony
await 0;
x += 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Sin embargo, su caso es un poco más complicado.
Has puesto await
dentro de una expresión, que usa +=
.
Probablemente sepa que en JS x += y
es idéntico a x = (x + y)
. Usaré la última forma para una mejor comprensión:
let x = 0;
async function test() {
x = (x + await 5);
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Cuando el intérprete llega a esta línea ...
x = (x + await 5);
... comienza a evaluarlo, y se convierte en ...
x = (0 + await 5);
... entonces, llega al await
y se detiene.
El código después de la llamada a la función comienza a ejecutarse y modifica el valor de x
, luego lo registra.
x
es ahora 1
.
Luego, una vez que sale el guión principal, el intérprete vuelve a la test
función en pausa y continúa evaluando esa línea:
x = (0 + 5);
Y, dado que el valor de x
ya está sustituido, permanece 0
.
Por último, el intérprete hace la adición, tiendas 5
de x
, y lo registra.
Puede verificar este comportamiento iniciando sesión dentro de un objeto getter / setter (en este ejemplo, y.z
refleja el 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)
yx += await 5
.