Biorąc pod uwagę poniższe przykłady kodu, czy jest jakaś różnica w zachowaniu, a jeśli tak, to jakie są te różnice?
return await promise
async function delay1Second() {
return (await delay(1000));
}
return promise
async function delay1Second() {
return delay(1000);
}
Jak rozumiem, pierwsza z nich miałaby obsługę błędów w ramach funkcji asynchronicznej, a błędy wypływałyby z obietnicy funkcji asynchronicznej. Jednak druga wymagałaby o jeden tik mniej. Czy to jest poprawne?
Ten fragment jest zwykłą funkcją zwracającą Obietnicę w celach informacyjnych.
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
javascript
async-await
PitaJ
źródło
źródło
async
z drugiej (return promise
) próbki.promise.then(() => nestedPromise)
spłaszczy się i „podąży” zanestedPromise
. Ciekawe, czym różni się od zagnieżdżonych zadań w C #, w których musielibyśmy toUnwrap
zrobić. Na marginesie wydaje się, żeawait somePromise
wywołujePromise.resolve(somePromise).then
, a nie tylkosomePromise.then
, z pewnymi interesującymi różnicami semantycznymi.Odpowiedzi:
W większości przypadków nie ma zauważalnej różnicy między
return
areturn await
. Obie wersjedelay1Second
mają dokładnie to samo obserwowalne zachowanie (ale w zależności od implementacjireturn await
wersja może zużywać nieco więcej pamięci, ponieważPromise
może zostać utworzony obiekt pośredni ).Jednak, jak zauważył @PitaJ, jest jeden przypadek, w którym występuje różnica: jeśli
return
lubreturn await
jest zagnieżdżony wtry
-catch
bloku. Rozważmy ten przykładW pierwszej wersji funkcja async oczekuje odrzuconej obietnicy przed zwróceniem jej wyniku, co powoduje, że odrzucenie zostaje zamienione w wyjątek i
catch
klauzula zostaje osiągnięta; funkcja zwróci w ten sposób obietnicę składającą się z ciągu „Zapisano!”.Jednak druga wersja funkcji zwraca odrzuconą obietnicę bezpośrednio, bez oczekiwania na nią w funkcji asynchronicznej , co oznacza, że
catch
sprawa nie jest wywoływana, a wywołujący otrzymuje odrzucenie.źródło
return new Promise(function(resolve, reject) { })
wfor...of
pętli, a następnie wywołanieresolve()
wewnątrz pętli po apipe()
nie wstrzymuje wykonywania programu do zakończenia potoku, zgodnie z życzeniem, jednak użycieawait new Promise(...)
robi. czy ta ostatnia jest nawet poprawna / poprawna składnia? czy to jest „skrótowe”return await new Promise(...)
? czy możesz mi pomóc zrozumieć, dlaczego to drugie działa, a pierwsze nie? w kontekście scenariusza jestsolution 02
od tej odpowiedziJak wspomniano w innych odpowiedziach, prawdopodobnie istnieje niewielka poprawa wydajności, gdy obietnica pojawi się, zwracając ją bezpośrednio - po prostu dlatego, że nie musisz najpierw czekać na wynik, a następnie zawijać go ponownie. Jednak nikt jeszcze nie mówił o optymalizacji połączeń końcowych .
Optymalizacja wywołań ogonowych lub „właściwe wywołania ogonowe” to technika, której interpreter używa do optymalizacji stosu wywołań. Obecnie niewiele środowisk wykonawczych jeszcze go obsługuje - mimo że jest technicznie częścią standardu ES6 - ale możliwe jest, że w przyszłości zostanie dodana obsługa, więc możesz się do tego przygotować, pisząc dobry kod w teraźniejszości.
Krótko mówiąc, TCO (lub PTC) optymalizuje stos wywołań, nie otwierając nowej ramki dla funkcji, która jest bezpośrednio zwracana przez inną funkcję. Zamiast tego ponownie wykorzystuje tę samą ramkę.
Ponieważ
delay()
jest zwracana bezpośrednio przezdelay1Second()
, środowiska wykonawcze obsługujące PTC najpierw otworzą ramkę dladelay1Second()
(funkcji zewnętrznej), ale zamiast otwierać kolejną ramkę dladelay()
(funkcji wewnętrznej), po prostu ponownie wykorzystają tę samą ramkę, która została otwarta dla funkcji zewnętrznej. Optymalizuje to stos, ponieważ może zapobiec przepełnieniu stosu (hehe) z bardzo dużymi funkcjami rekurencyjnymi, npfibonacci(5e+25)
.. Zasadniczo staje się pętlą, która jest znacznie szybsza.PTC jest aktywowane tylko wtedy, gdy funkcja wewnętrzna jest zwracana bezpośrednio . Nie jest używany, gdy wynik funkcji jest zmieniany przed jego zwróceniem, na przykład jeśli miałeś
return (delay(1000) || null)
lubreturn await delay(1000)
.Ale jak powiedziałem, większość środowisk wykonawczych i przeglądarek nie obsługuje jeszcze PTC, więc prawdopodobnie nie robi to teraz dużej różnicy, ale nie zaszkodzi, aby Twój kod był zabezpieczony w przyszłości.
Przeczytaj więcej w tym pytaniu: Node.js: Czy istnieją optymalizacje wywołań ogonowych w funkcjach asynchronicznych?
źródło
To trudne pytanie, ponieważ w praktyce zależy to od tego, jak (prawdopodobnie
babel
) faktycznie renderuje transpilerasync/await
. Rzeczy, które są jasne niezależnie:Obie implementacje powinny zachowywać się tak samo, chociaż pierwsza implementacja może mieć o jedną mniej
Promise
w łańcuchu.Zwłaszcza jeśli
await
usuniesz niepotrzebne , druga wersja nie wymagałaby dodatkowego kodu z transpilera, podczas gdy pierwsza wymaga.Zatem z punktu widzenia wydajności kodu i debugowania preferowana jest druga wersja, choć tylko nieznacznie, podczas gdy pierwsza wersja ma niewielką korzyść w zakresie czytelności, ponieważ wyraźnie wskazuje, że zwraca obietnicę.
źródło
undefined
), a druga zwraca aPromise
.async/await
- o wiele trudniej mi o tym myśleć. @PitaJ jest poprawne, obie funkcje zwracają obietnicę.try-catch
? W takimreturn promise
razie żadenrejection
nie zostałby złapany, prawda, podczas gdy w tymreturn await promise
przypadku byłoby, prawda?await
każdy z nich w jakiejś witrynie telefonicznej, wynik będzie bardzo różny.Zauważalna różnica: odrzucenie obietnicy jest obsługiwane w różnych miejscach
return somePromise
przekaże część obietnicy do strony wywołującej, aawait
inną obietnicę rozliczenia w miejscu wywołania (jeśli taka istnieje). Dlatego jeśli jakaś obietnica zostanie odrzucona, nie będzie obsługiwana przez lokalny blok catch, ale przez blok catch strony wywołania.return await somePromise
najpierw zaczeka na jakąś obietnicę, że osiądzie lokalnie. Dlatego wartość lub wyjątek będą najpierw obsługiwane lokalnie. => Lokalny blok catch zostanie wykonany, jeślisomePromise
zostanie odrzucony.Powód:
return await Promise
czeka lokalnie i na zewnątrz,return Promise
czeka tylko na zewnątrzSzczegółowe kroki:
powrót Obietnica
delay1Second()
;delay1Second()
functiondelay(1000)
zwraca obietnicę natychmiast z[[PromiseStatus]]: 'pending
. Nazwijmy todelayPromise
.Promise.resolve()
( Źródło ). Ponieważdelay1Second
jest to funkcja asynchroniczna, mamy:Promise.resolve(delayPromise)
zwracadelayPromise
bez robienia czegokolwiek, ponieważ dane wejściowe są już obietnicą (zobacz MDN Promise.resolve ):await
czeka, aż sprawadelayPromise
zostanie rozstrzygnięta.delayPromise
spełnia PromiseValue = 1:delayPromise
jest odrzucane:powrót czekaj na obietnicę
delay1Second()
;delay1Second()
functiondelay(1000)
zwraca obietnicę natychmiast z[[PromiseStatus]]: 'pending
. Nazwijmy todelayPromise
.delayPromise
zostanie rozliczone.delayPromise
jest spełniony przy PromiseValue = 1:delayPromise
jest odrzucony:Słownik:
Promise.[[PromiseStatus]]
zmiany zpending
naresolved
lubrejected
źródło
tutaj zostawiam trochę kodu praktycznego, abyś mógł zrozumieć i to różnicę
funkcja "x" jest po prostu funkcją asynchroniczną niż ma inne funkcje, jeśli usunie zwrot, wypisze "więcej kodu ..."
zmienna x jest po prostu funkcją asynchroniczną, która z kolei ma inną funkcję asynchroniczną, w głównym kodzie wywołujemy oczekiwanie na wywołanie funkcji zmiennej x, po zakończeniu następuje zgodnie z sekwencją kodu, to byłoby normalne dla „async / await”, ale wewnątrz funkcji x jest inna funkcja asynchroniczna, która zwraca obietnicę lub zwraca „obietnicę”, pozostanie wewnątrz funkcji x, zapominając o głównym kodzie, to znaczy nie wydrukuje "console.log (" więcej kodu .. "), z drugiej strony, jeśli wstawimy" await ", będzie on czekał na każdą funkcję, która zakończy się i na końcu podąży za normalną sekwencją głównego kodu.
poniżej "console.log (" zakończone 1 ", usuń" powrót ", zobaczysz zachowanie.
źródło
Oto przykład maszynopisu, który możesz uruchomić i przekonać się, że potrzebujesz tego „return await”
źródło