let x = 0;
async function test() {
x += await 5;
console.log('x :', x);
}
test();
x += 1;
console.log('x :', x);
x
Zarejestrowane wartości to 1
i 5
. Moje pytanie brzmi: dlaczego wartość x
5
drugiego dziennika?
Jeśli polecenie test
jest wykonywane po x += 1
(ponieważ jest to funkcja asynchroniczna), wówczas wartość x wynosi 1 do czasu test
wykonania, więc x += await 5
należy podać wartość x
6
.
javascript
async-await
event-loop
ALDRIN P VINCENT
źródło
źródło
await (x += 5)
ix += await 5
.Odpowiedzi:
TL; DR: Ponieważ
+=
czytax
wcześniej, ale zapisuje go po zmianie, ze względu naawait
słowo kluczowe w drugim operandzie (po prawej stronie).async
funkcje działają synchronicznie, gdy wywoływane są do pierwszejawait
instrukcji.Jeśli więc usuniesz
await
, zachowuje się jak normalna funkcja (z wyjątkiem tego, że nadal zwraca obietnicę).W takim przypadku, można uzyskać
5
i6
w konsoli:Pierwszy
await
przestaje działać synchronicznie, nawet jeśli jego argument jest dostępny synchronicznie, więc zostaną zwrócone następujące elementy1
i6
, zgodnie z oczekiwaniami:Twoja sprawa jest jednak nieco bardziej skomplikowana.
Wstawiłeś
await
wyrażenie, które używa+=
.Prawdopodobnie wiesz, że w JS
x += y
jest identycznyx = (x + y)
. Użyję tego drugiego formularza dla lepszego zrozumienia:Kiedy tłumacz osiągnie tę linię ...
... zaczyna to oceniać i przechodzi w ...
... wtedy osiąga
await
i zatrzymuje się.Kod po wywołaniu funkcji zaczyna działać, modyfikuje wartość
x
, a następnie rejestruje ją.x
jest teraz1
.Następnie, po zakończeniu skryptu głównego, interpreter wraca do wstrzymanej
test
funkcji i kontynuuje ocenę tej linii:A ponieważ wartość
x
jest już podstawiona, pozostaje0
.Wreszcie, interpreter robi dodatek, przechowuje
5
sięx
i rejestruje go.Możesz sprawdzić to zachowanie, logując się w obiekcie pobierającym / ustawiającym właściwości obiektu (w tym przykładzie
y.z
odzwierciedla wartośćx
:źródło
x += y
jest identyczne zx = (x + y)
”. - Nie dzieje się tak w każdej sytuacji w każdym języku, ale ogólnie można liczyć na to, że zachowają się tak samo.Twoje oświadczenie
x += await 5
schodzi doWartość
_temp
początkowa to0
, a jeśli zmienisz sięx
podczasawait
(co robi twój kod), to nie ma znaczenia, zostanie ona5
później przypisana .źródło
Kod ten jest dość skomplikowany, ponieważ wymaga nieoczekiwanych skoków asynchronicznych tam i z powrotem. Przyjrzyjmy się (blisko), jak to będzie faktycznie wykonane, a potem wyjaśnię, dlaczego. Zmieniłem również dzienniki konsoli, aby dodać liczbę - ułatwia to odwoływanie się do nich, a także pokazuje lepiej, co jest rejestrowane:
Tak więc kod nie jest właściwie prosty, to na pewno. I mamy też dziwną
4/7
rzecz. I to jest naprawdę cały problem tutaj.Po pierwsze, wyjaśnijmy - funkcje asynchroniczne nie są tak naprawdę ściśle asynchroniczne. Zatrzymaliby wykonanie i wznowiliby później, jeśli
await
zostanie użyte słowo kluczowe. Bez niego wykonują synchronicznie od góry do dołu, wyrażenie po wyrażeniu synchronicznie:Pierwszą rzeczą, którą musimy wiedzieć, że użycie
await
spowoduje, że reszta funkcji zostanie wykonana później. W podanym przykładzie oznacza to, żeconsole.log('x1 :', x)
zostanie wykonane po reszcie kodu synchronicznego. Jest tak, ponieważ wszelkie obietnice zostaną rozpatrzone po zakończeniu bieżącej pętli zdarzeń.To wyjaśnia, dlaczego najpierw jesteśmy
x2 : 1
logowani i dlaczego logujemy się na drugim, ale nie dlaczego ta ostatnia wartość jest . Logicznie powinno być ... ale tutaj jest drugi haczyk do słowa kluczowego - wstrzyma wykonanie funkcji, ale wszystko, zanim jeszcze się uruchomi. będzie przetwarzany w następujący sposóbx2 : 5
5
x += await 5
5
await
x += await 5
x
. To w momencie egzekucji0
.await
następne wyrażenie, które jest5
. Tak więc funkcja zatrzymuje się teraz i zostanie wznowiona później.0 + 5
x
Tak, funkcja pauzy po niej przeczytać, że
x
jest0
i wznawia, gdy jest już zmieniona, jednakże nie ponownie odczytać wartośćx
.Gdybyśmy rozpakować
await
doPromise
ekwiwalentu, które wykonać, trzeba:źródło
Tak, to trochę skomplikowane, tak naprawdę dzieje się to, że obie operacje dodawania odbywają się parellaly, więc operacja wyglądałaby następująco:
W ramach obietnicy:
x += await 5
==>x = x + await 5
==>x = 0 + await 5
==>5
Na zewnątrz:
x += 1
==>x = x + 1
==>x = 0 + 1
==>1
ponieważ wszystkie powyższe operacje odbywają się od lewej do prawej, pierwszą część dodawania można obliczyć w tym samym czasie, a ponieważ przed 5 jest oczekiwanie, dodanie może nieco się opóźnić. Wykonanie można zobaczyć, umieszczając punkt przerwania w kodzie.
źródło
Async i Await są przedłużeniami obietnic. Funkcja asynchroniczna może zawierać wyrażenie oczekujące, które wstrzymuje wykonanie funkcji asynchronicznej i czeka na przekazaną rozdzielczość Obietnicy, a następnie wznawia wykonywanie funkcji asynchronicznej i zwraca rozstrzygniętą wartość. Pamiętaj, że słowo kluczowe oczekujące jest ważne tylko w funkcjach asynchronicznych.
Nawet jeśli zmieniłeś wartość x po wywołaniu funkcji testowej, nadal wartość x pozostanie równa 0, ponieważ funkcja asynchroniczna już utworzyła nową instancję. Oznacza to, że wszystko zmienia się na zmiennej poza nią, nie zmieni wartości wewnątrz niej po jej wywołaniu. Chyba że ustawisz swój przyrost powyżej funkcji testowej.
źródło
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)
WyprowadzaReceived synchronous change
iReceived change