let x = 0;
async function test() {
x += await 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Los valores de x
logueado son 1
y 5
. Mi pregunta es: ¿por qué es el valor de x
5
en el segundo registro?
Si test
se ejecuta después x += 1
(dado que es una función asíncrona), el valor de x es 1 en el momento en que test
se ejecuta, por lo que x += await 5
debe ser el valor de x
6
.
javascript
async-await
event-loop
ALDRIN P VINCENT
fuente
fuente
await (x += 5)
yx += await 5
.Respuestas:
TL; DR: Porque
+=
leex
antes, pero lo escribe después de que ha cambiado, debido a laawait
palabra clave en su segundo operando (lado derecho).async
Las funciones se ejecutan sincrónicamente cuando llaman hasta la primeraawait
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
y6
en la consola:El primero
await
detiene la ejecución síncrona, incluso si su argumento está disponible sincrónicamente, por lo que se devolverá lo siguiente1
y6
, como es de esperar: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 ax = (x + y)
. Usaré la última forma para una mejor comprensión:Cuando el intérprete llega a esta línea ...
... comienza a evaluarlo, y se convierte en ...
... 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 ahora1
.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:Y, dado que el valor de
x
ya está sustituido, permanece0
.Por último, el intérprete hace la adición, tiendas
5
dex
, y lo registra.Puede verificar este comportamiento iniciando sesión dentro de un objeto getter / setter (en este ejemplo,
y.z
refleja el valor dex
:fuente
x += y
es idéntico ax = (x + y)
". - Este no es el caso en todas las situaciones en todos los idiomas, pero en general puede contar con que actúen de la misma manera.Su declaración se
x += await 5
desugará aEl
_temp
valor orario es0
, y si cambiax
duranteawait
(lo que hace su código) no importa, se asigna5
después.fuente
Este código es bastante complejo de seguir porque requiere algunos saltos asíncronos inesperados de un lado a otro. Examinemos (cerca de) cómo se ejecutará realmente y luego explicaré por qué. También he cambiado los registros de la consola para agregar un número, lo que facilita la referencia a ellos y también muestra mejor lo que se registra:
Por lo tanto, el código en realidad no funciona de manera directa, eso es seguro. Y también tenemos
4/7
algo extraño . Y eso es realmente la totalidad del problema aquí.En primer lugar, aclaremos: las funciones asincrónicas no son realmente estrictamente asincrónicas. Solo pausarían la ejecución y reanudarían más tarde si
await
se usa la palabra clave. Sin ella, ejecutan de arriba a abajo, expresión tras expresión sincrónicamente:Entonces, lo primero que necesitamos saber es que usar
await
hará que el resto de la función se ejecute más tarde. En el ejemplo dado, eso significa queconsole.log('x1 :', x)
se ejecutará después del resto del código síncrono. Esto se debe a que cualquier Promesa se resolverá una vez que finalice el ciclo del evento actual.Entonces, esto explica por qué nos
x2 : 1
registramos primero y por quéx2 : 5
se registra en segundo lugar, pero no por qué es el último valor5
. Lógicamentex += await 5
debería ser5
... pero aquí está la segunda captura de laawait
palabra clave: pausará la ejecución de la función, pero cualquier cosa antes de que ya se haya ejecutado.x += await 5
en realidad se va a procesar de la siguiente manerax
. En el momento de la ejecución, eso es0
.await
La siguiente expresión que es5
. Por lo tanto, la función se detiene ahora y se reanudará más adelante.0 + 5
x
Por lo tanto, las pausas función después de que leyeron que
x
es0
y se reanuda cuando ya ha cambiado, sin embargo, no volver a leer el valor dex
.Si desenvolvemos el equivalente
await
en elPromise
equivalente que se ejecutaría, tiene:fuente
Sí, es un poco complicado lo que realmente está sucediendo, ambas operaciones de suma están sucediendo de manera paralela, por lo que la operación sería como:
Dentro de la promesa:
x += await 5
==>x = x + await 5
==>x = 0 + await 5
==>5
Afuera:
x += 1
==>x = x + 1
==>x = 0 + 1
==>1
Como todas las operaciones anteriores se realizan de izquierda a derecha, la primera parte de la suma puede calcularse al mismo tiempo y dado que hay una espera antes de las 5, esa adición puede retrasarse un poco. Puede ver la ejecución colocando un punto de interrupción dentro del código.
fuente
Async y Await son extensiones de promesas. Una función asincrónica puede contener una expresión de espera que detiene la ejecución de la función asincrónica y espera la resolución de la Promesa aprobada, y luego reanuda la ejecución de la función asincrónica y devuelve el valor resuelto. Recuerde, la palabra clave await solo es válida dentro de las funciones asíncronas.
Incluso si ha cambiado el valor de x después de llamar a la función de prueba, el valor de x seguirá siendo 0 porque la función asincrónica ya ha creado su nueva instancia. Lo que significa que todo cambia en la variable fuera de ella no cambiará el valor dentro de ella después de que se llamó. A menos que ponga su incremento por encima de la función de prueba.
fuente
let x="Didn't receive change"; (async()=>{await 'Nothing'; console.log(x); await new Promise(resolve=>setTimeout(resolve,2000)); console.log(x)})(); x='Received synchronous change'; setTimeout(()=>{x='Received change'},1000)
GeneraReceived synchronous change
yReceived change