let x = 0;
async function test() {
x += await 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
Die Werte von x
protokolliert sind 1
und 5
. Meine Frage ist: Warum ist der Wert von x
5
im zweiten Protokoll?
Wenn das test
nach ausgeführt wird x += 1
(da es sich um eine asynchrone Funktion handelt), ist der Wert von x zum Zeitpunkt der test
Ausführung 1, daher x += await 5
sollte der Wert von angegeben werden x
6
.
javascript
async-await
event-loop
ALDRIN P VINCENT
quelle
quelle
await (x += 5)
und kennenx += await 5
.Antworten:
TL; DR: Weil zuvor
+=
gelesenx
, aber geschrieben, nachdem es sich geändert hat, aufgrund desawait
Schlüsselworts in seinem zweiten Operanden (rechts).async
Funktionen werden synchron ausgeführt, wenn sie bis zur erstenawait
Anweisung aufgerufen werden .Wenn Sie also entfernen
await
, verhält es sich wie eine normale Funktion (mit der Ausnahme, dass es immer noch ein Versprechen zurückgibt).In diesem Fall erhalten Sie
5
und6
in der Konsole:Der erste
await
Vorgang stoppt die synchrone Ausführung, auch wenn sein Argument synchron verfügbar ist. Daher wird Folgendes zurückgegeben1
und6
wie erwartet:Ihr Fall ist jedoch etwas komplizierter.
Sie haben
await
einen Ausdruck eingefügt, der verwendet+=
.Sie wissen wahrscheinlich, dass in JS
x += y
identisch ist mitx = (x + y)
. Ich werde das letztere Formular zum besseren Verständnis verwenden:Wenn der Dolmetscher diese Zeile erreicht ...
... es beginnt es zu bewerten und es dreht sich um ...
... dann erreicht es das
await
und hört auf.Der Code nach dem Funktionsaufruf wird ausgeführt, ändert den Wert von
x
und protokolliert ihn anschließend.x
ist jetzt1
.Nach dem Beenden des Hauptskripts kehrt der Interpreter zur angehaltenen
test
Funktion zurück und setzt die Auswertung dieser Zeile fort:Und da der Wert von
x
bereits ersetzt ist, bleibt er bestehen0
.Schließlich führt der Interpreter die Addition durch, speichert
5
siex
und protokolliert sie.Sie können dieses Verhalten überprüfen, indem Sie sich in einem Objekt-Eigenschafts-Getter / -Setter anmelden (in diesem Beispiel wird
y.z
der Wert vonx
:quelle
x += y
ist identisch mitx = (x + y)
." - Dies ist nicht in jeder Situation in jeder Sprache der Fall, aber im Allgemeinen können Sie sich darauf verlassen, dass sie gleich handeln.Ihre Aussage
x += await 5
Desugars zuDer
_temp
oräre Wert ist0
, und wenn Sie sichx
während desawait
(was Ihr Code tut) ändern, spielt es keine Rolle, er wird5
danach zugewiesen .quelle
Dieser Code ist recht komplex zu befolgen, da einige unerwartete asynchrone Sprünge hin und her erforderlich sind. Lassen Sie uns untersuchen (in der Nähe), wie es tatsächlich ausgeführt wird, und ich werde anschließend erklären, warum. Ich habe auch die Konsolenprotokolle geändert, um eine Nummer hinzuzufügen. Dies erleichtert das Verweisen auf sie und zeigt auch besser, was protokolliert wird:
Der Code läuft also nicht gerade, das ist sicher. Und wir haben auch eine seltsame
4/7
Sache. Und das ist wirklich die Gesamtheit des Problems hier.Lassen Sie uns zunächst klarstellen, dass asynchrone Funktionen nicht streng asynchron sind. Sie würden die Ausführung nur anhalten und später fortsetzen, wenn das
await
Schlüsselwort verwendet wird. Ohne sie führen sie Ausdruck für Ausdruck synchron von oben nach unten aus:Also, das erste , was müssen wir wissen , dass die Verwendung von
await
machen den Rest später der Funktion auszuführen. In dem gegebenen Beispiel, dass die Mittel , dieconsole.log('x1 :', x)
ausgeführt wird , nachdem der Rest des Synchroncodes. Dies liegt daran, dass alle Versprechen nach Abschluss der aktuellen Ereignisschleife aufgelöst werden.Dies erklärt also, warum wir zuerst
x2 : 1
protokolliert werden und warum zweitens protokolliert wird, aber nicht, warum der letztere Wert ist . Logischerweise sollte ... aber hier ist der zweite Haken zum Keyword - es wird unterbricht die Ausführung der Funktion , sondern etwas , bevor es schon gelaufen ist. wird tatsächlich auf folgende Weise verarbeitetx2 : 5
5
x += await 5
5
await
x += await 5
x
. Zum Zeitpunkt der Hinrichtung ist das so0
.await
der nächste Ausdruck, der ist5
. Die Funktion wird jetzt angehalten und später fortgesetzt.0 + 5
x
Also, lesen Sie die Funktion pausiert , nachdem es das
x
ist0
und wird fortgesetzt , wenn ist es schon geändert, aber es nicht erneut zu lesen , den Wertx
.Wenn wir das
await
in dasPromise
Äquivalent auspacken , das ausgeführt werden würde, haben Sie:quelle
Ja, es ist ein bisschen schwierig, was tatsächlich passiert, wenn beide Additionsoperationen parallel ablaufen, so dass die Operation wie folgt aussehen würde:
Innerhalb des Versprechens:
x += await 5
==>x = x + await 5
==>x = 0 + await 5
==>5
Draußen:
x += 1
==>x = x + 1
==>x = 0 + 1
==>1
Da alle oben genannten Operationen von links nach rechts ausgeführt werden, kann der erste Teil der Addition gleichzeitig berechnet werden, und da vor 5 gewartet wird, kann sich die Addition etwas verzögern. Sie können die Ausführung sehen, indem Sie einen Haltepunkt in den Code einfügen.
quelle
Async und Await sind Erweiterungen von Versprechungen. Eine asynchrone Funktion kann einen Warteausdruck enthalten, der die Ausführung der asynchronen Funktion anhält und auf die Auflösung des übergebenen Versprechens wartet. Anschließend wird die Ausführung der asynchronen Funktion fortgesetzt und der aufgelöste Wert zurückgegeben. Denken Sie daran, dass das Schlüsselwort await nur in asynchronen Funktionen gültig ist.
Selbst wenn Sie den Wert von x nach dem Aufrufen der Testfunktion geändert haben, bleibt der Wert von x 0, da die asynchrone Funktion bereits ihre neue Instanz erstellt hat. Das heißt, alles, was sich an der Variablen außerhalb der Variablen ändert, ändert den Wert innerhalb der Variablen nach dem Aufruf nicht. Es sei denn, Sie setzen Ihr Inkrement über die Testfunktion.
quelle
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)
SieReceived synchronous change
Received change