let x = 0;
async function test() {
x += await 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Les valeurs x
enregistrées sont 1
et 5
. Ma question est: pourquoi la valeur de x
5
sur le deuxième journal?
Si le test
est exécuté après x += 1
(puisqu'il s'agit d'une fonction asynchrone), alors la valeur de x est 1 au moment de l' test
exécution, donc x += await 5
devrait prendre la valeur de x
6
.
javascript
async-await
event-loop
ALDRIN P VINCENT
la source
la source
await (x += 5)
etx += await 5
.Réponses:
TL; DR: Parce que
+=
litx
avant, mais l'écrit après qu'il a changé, en raison duawait
mot - clé dans son deuxième opérande (côté droit).async
les fonctions s'exécutent de manière synchrone lors de leur appel jusqu'à la premièreawait
instruction.Donc, si vous supprimez
await
, il se comporte comme une fonction normale (à l'exception qu'il renvoie toujours une promesse).Dans ce cas, vous obtenez
5
et6
dans la console:Le premier
await
arrête l'exécution synchrone, même si son argument est disponible de manière synchrone, donc ce qui suit reviendra1
et6
, comme vous vous y attendez:Cependant, votre cas est un peu plus compliqué.
Vous avez mis à l'
await
intérieur une expression, qui utilise+=
.Vous savez probablement que JS
x += y
est identique àx = (x + y)
. Je vais utiliser ce dernier formulaire pour une meilleure compréhension:Lorsque l'interprète atteint cette ligne ...
... il commence à l'évaluer, et il se transforme en ...
... puis, il atteint le
await
et s'arrête.Le code après l'appel de fonction commence à s'exécuter et modifie la valeur de
x
, puis l'enregistre.x
est maintenant1
.Ensuite, après la fin du script principal, l'interpréteur revient à la
test
fonction suspendue et continue d'évaluer cette ligne:Et, puisque la valeur de
x
est déjà substituée, elle reste0
.Enfin, l'interprète fait l'ajout, les magasins
5
àx
, et l' enregistre.Vous pouvez vérifier ce comportement en vous connectant à l'intérieur d'un getter / setter de propriété d'objet (dans cet exemple,
y.z
reflète la valeur dex
:la source
x += y
identique àx = (x + y)
." - Ce n'est pas le cas dans toutes les situations dans toutes les langues, mais en général, vous pouvez compter sur eux agissant de la même manière.Votre déclaration
x += await 5
desugars àLa
_temp
valeur orale est0
, et si vous changezx
pendantawait
(ce que fait votre code), cela n'a pas d'importance, elle est attribuée5
par la suite.la source
Ce code est assez complexe à suivre car il nécessite des sauts asynchrones inattendus d'avant en arrière. Examinons (de près) comment cela va réellement être exécuté et j'expliquerai pourquoi par la suite. J'ai également modifié les journaux de la console pour ajouter un nombre - facilite la référence à ceux-ci et montre également mieux ce qui est enregistré:
Donc, le code ne se déroule pas de manière directe, c'est sûr. Et nous avons aussi une
4/7
chose étrange . Et c'est vraiment tout le problème ici.Tout d'abord, clarifions - les fonctions asynchrones ne sont pas réellement strictement asynchrones. Ils ne suspendent l'exécution et ne reprennent plus tard que si le
await
mot clé est utilisé. Sans cela, ils exécutent de haut en bas, expression après expression de manière synchrone:Donc, la première chose que nous devons savoir, c'est que l'utilisation
await
fera exécuter le reste de la fonction plus tard. Dans l'exemple donné, cela signifie queconsole.log('x1 :', x)
va être exécuté après le reste du code synchrone. En effet, toutes les promesses seront résolues après la fin de la boucle d'événements en cours.Donc, cela explique pourquoi nous nous
x2 : 1
connectons en premier et pourquoix2 : 5
est enregistré en deuxième mais pas pourquoi cette dernière valeur est5
. Logiquementx += await 5
devrait être5
... mais voici le deuxième hic auawait
mot-clé - il suspendra l'exécution de la fonction mais n'importe quoi avant qu'il ne soit déjà exécuté.x += await 5
va en fait être traité de la manière suivantex
. Au moment de l'exécution, c'est0
.await
l'expression suivante qui est5
. Ainsi, la fonction s'arrête maintenant et sera reprise plus tard.0 + 5
x
Ainsi, la fonction fait une pause après avoir lu ce qui
x
est0
et reprend lorsqu'elle est déjà modifiée, mais elle ne relit pas la valeur dex
.Si nous déballons le
await
dans l'Promise
équivalent qui s'exécuterait, vous avez:la source
Il est un peu difficile de savoir ce qui se passe réellement, c'est que les deux opérations d'addition se déroulent en parallèle, de sorte que l'opération serait comme:
Dans les limites de la promesse:
x += await 5
==>x = x + await 5
==>x = 0 + await 5
==>5
À l'extérieur:
x += 1
==>x = x + 1
==>x = 0 + 1
==>1
étant donné que toutes les opérations ci-dessus se déroulent de gauche à droite, la première partie de l'addition peut être calculée en même temps et puisqu'il y a une attente avant 5, l'addition peut retarder un peu. Vous pouvez voir l'exécution en plaçant un point d'arrêt dans le code.
la source
Async et Await sont des extensions de promesses. Une fonction asynchrone peut contenir une expression d'attente qui suspend l'exécution de la fonction asynchrone et attend la résolution de Promise passée, puis reprend l'exécution de la fonction asynchrone et renvoie la valeur résolue. N'oubliez pas que le mot-clé wait n'est valide que dans les fonctions asynchrones.
Même si vous avez modifié la valeur de x après avoir appelé la fonction de test, la valeur de x restera toujours 0 car la fonction asynchrone a déjà créé sa nouvelle instance. Ce qui signifie que tout change sur la variable à l'extérieur de celle-ci ne changera pas la valeur à l'intérieur de celle-ci après son appel. Sauf si vous placez votre incrément au-dessus de la fonction de test.
la source
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)
Il sortReceived synchronous change
etReceived change