Jaka jest różnica między obietnicami JavaScript a oczekiwaniem asynchronicznym?

103

Używałem już funkcji ECMAScript 6 i ECMAScript 7 (dzięki Babel) w moich aplikacjach - zarówno mobilnych, jak i internetowych.

Pierwszym krokiem było oczywiście przejście na poziomy ECMAScript 6. Nauczyłem się wielu wzorców asynchronicznych, obietnic (które są naprawdę obiecujące), generatorów (nie wiem, dlaczego symbol *) itd. Z tego obietnice całkiem dobrze pasowały do ​​mojego celu. Używam ich dość często w moich aplikacjach.

Oto przykład / pseudokod pokazujący, jak zaimplementowałem podstawową obietnicę-

var myPromise = new Promise(
    function (resolve,reject) {
      var x = MyDataStore(myObj);
      resolve(x);
    });

myPromise.then(
  function (x) {
    init(x);
});

W miarę upływu czasu, natknąłem ECMAScript 7 możliwości, a jedną z nich jest ASYNCi AWAITsłowa kluczowe / funkcje. Te w połączeniu czynią wielkie cuda. Zacząłem zastępować niektóre obietnice async & await. Wydaje się, że dodają wielką wartość do stylu programowania.

Ponownie, oto pseudokod pokazujący, jak wygląda moja funkcja async, await-

async function myAsyncFunction (myObj) {
    var x = new MyDataStore(myObj);
    return await x.init();
}
var returnVal = await myAsyncFunction(obj);

Pomijając błędy składniowe (jeśli występują), obaj robią dokładnie to samo, co ja czuję. Prawie udało mi się zastąpić większość moich obietnic asynchronicznym, czeka.

Dlaczego asynchroniczne oczekiwanie jest potrzebne, gdy obietnice mają podobną pracę?

Czy async, w oczekiwaniu na rozwiązanie większego problemu? A może było to po prostu inne rozwiązanie do piekła zwrotnego?

Jak powiedziałem wcześniej, jestem w stanie korzystać z obietnic i asynchronii, czekam na rozwiązanie tego samego problemu. Czy jest coś konkretnego, co czeka na rozwiązanie async?

Dodatkowe uwagi:

W moich projektach React i modułach Node.js intensywnie korzystałem z asynchronii, oczekiwań i obietnic. Szczególnie React byli wczesnym ptaszkiem i przyjęli wiele funkcji ECMAScript 6 i ECMAScript 7.

bozzmob
źródło
3
Wygląda na to, że Twój pierwszy blok kodu korzysta z obietnicy operacji synchronicznej. Dlaczego chcesz to zrobić? Synchronizacja jest z natury łatwiejsza do napisania kodu, więc nie powinno być powodu, aby zawijać operację synchroniczną w obietnicę i wymuszać jej asynchroniczność.
jfriend00
@ jfriend00 Tak, masz rację. Edytował kod. Dzięki.
bozzmob
2
Wciąż próbujesz używać narzędzi asynchronicznych z funkcjami synchronicznymi - teraz w obu blokach kodu. Czemu?
jfriend00
@ jfriend00 Ok. Tutaj mam mój kod gist.github.com/bozzmob/26d38b83dc37d1be37f5 . Czy możesz mi powiedzieć, co robię źle?
bozzmob
10
Wygląda na to, że wystarczy poczytać, aby zrozumieć, do czego służą asynchronizacja i oczekiwanie. Oto kilka artykułów: Długa droga do Async / Await w JavaScript i upraszczanie kodowania asynchronicznego za pomocą funkcji asynchronicznych ES7 i oswajanie asynchronicznej bestii za pomocą ES7 .
jfriend00

Odpowiedzi:

82

Dlaczego potrzebna jest asynchronizacja i oczekiwanie, skoro Promises wykonuje podobną pracę? Czy async, w oczekiwaniu na rozwiązanie większego problemu?

async/awaitpo prostu zapewnia synchroniczne wrażenie kodu asynchronicznego. To bardzo elegancka forma cukru syntaktycznego.

W przypadku prostych zapytań i manipulacji danymi Obietnice mogą być proste, ale jeśli napotkasz scenariusze, w których występuje złożona manipulacja danymi i nie jest to związane, łatwiej jest zrozumieć, co się dzieje, jeśli kod po prostu wygląda tak, jakby był synchroniczny (innymi słowy, składnia sama w sobie jest formą „przypadkowej złożoności”, którą async/awaitmożna obejść).

Jeśli chcesz wiedzieć, możesz użyć biblioteki takiej jak co(obok generatorów), aby uzyskać ten sam rodzaj wrażenia. Takie rzeczy zostały opracowane, aby rozwiązać problem, który async/awaitostatecznie rozwiązuje (natywnie).

Josh Beam
źródło
Czy możesz wyjaśnić, co oznacza „przypadkowa złożoność”? A jeśli chodzi o wydajność, nie ma między nimi różnicy?
bozzmob
@bozzmob, shaffner.us/cs/papers/tarpit.pdf <- wyjaśnia tam „przypadkową złożoność”. Jeśli chodzi o twoje pytanie dotyczące wydajności, wątpię w to, zwłaszcza że silnik V8 jest tym, czym jest. Jestem pewien, że są tam testy perfekcji, ale nie martwiłbym się tym zbytnio. Nie trać czasu na mikro-optymalizację, gdy nie jest to konieczne.
Josh Beam
1
Wielkie dzięki! Oto kilka wspaniałych informacji, które otrzymałem od Ciebie. I tak, nie zajmiemy się mikrooptymalizacjami.
bozzmob
Uważam, że to wyjaśnienie jest pomocne nikgrozev.com/2015/07/14/…
mwojtera
35

Async / Await zapewnia znacznie ładniejszą składnię w bardziej złożonych scenariuszach. W szczególności wszystko, co dotyczy pętli lub pewnych innych konstrukcji, takich jak try/ catch.

Na przykład:

while (!value) {
  const intermediate = await operation1();
  value = await operation2(intermediate);
}

Ten przykład byłby znacznie bardziej zawiły przy użyciu obietnic.

Stephen Cleary
źródło
To świetny przykład, aby zrozumieć to samo. Więc jeśli chodzi o wydajność, nie ma między nimi różnicy? A co jest lepsze w kodzie? Async Await wydaje się być lepsza po obejrzeniu przynajmniej twojego przykładu.
bozzmob
1
@bozzmob: Nie ma różnicy w wydajności. Jeśli nie masz nic przeciwko używaniu async / await, polecam to. Sam go jeszcze nie używam, ponieważ tak naprawdę nie jest częścią oficjalnego standardu.
Stephen Cleary
Tak, zgadzam się, że nie jest to część standardu, ale w przypadku ReactJS (reaguj konkretnie na język natywny), jestem zmuszony używać go w niektórych częściach kodu. Tak więc połowa z nich to obietnice, a połowa to asynchroniczne oczekiwania. Więc zadałem ci te pytania. Dzięki za potrzebne informacje.
bozzmob
1
Myślę, że wiele osób jest zdezorientowanych i / lub wprowadzonych w błąd, gdy nikt nie używa bloku try / catch w swoich próbkach kodu.
Augie Gardner
Masz na myśli to? const getValue = value => value || operation1().then(operation2).then(getValue);
Sharcoux
14

Dlaczego potrzebna jest asynchronizacja i oczekiwanie, skoro Promises wykonuje podobną pracę? Czy async, w oczekiwaniu na rozwiązanie większego problemu? czy to było po prostu inne rozwiązanie do piekła zwrotnego? Jak powiedziałem wcześniej, mogę używać Promises i Async, Oczekuj, aby rozwiązać ten sam problem. Czy jest coś konkretnego, co rozwiązało Async Await?

Pierwszą rzeczą, którą musisz zrozumieć, że async/ awaitsyntax to tylko cukier syntaktyczny, który ma na celu zwiększenie obietnic. W rzeczywistości zwracana wartość asyncfunkcji jest obietnicą. async/ awaitsyntax daje nam możliwość pisania asynchronicznego w sposób synchroniczny. Oto przykład:

Łańcuch obietnicy:

function logFetch(url) {
  return fetch(url)
    .then(response => response.text())
    .then(text => {
      console.log(text);
    }).catch(err => {
      console.error('fetch failed', err);
    });
}

Async funkcjonować:

async function logFetch(url) {
  try {
    const response = await fetch(url);
    console.log(await response.text());
  }
  catch (err) {
    console.log('fetch failed', err);
  }
}

W powyższym przykładzie awaitczeka na fetch(url)rozwiązanie lub odrzucenie obietnicy ( ). Jeśli obietnica zostanie rozwiązana, wartość jest przechowywana w responsezmiennej, a jeśli obietnica zostanie odrzucona, zgłosi błąd i w ten sposób wejdzie do catchbloku.

Już teraz widzimy, że użycie async/ awaitmoże być bardziej czytelne niż łączenie obietnic. Jest to szczególnie prawdziwe, gdy rośnie liczba obietnic, z których korzystamy. Zarówno łączenie obietnicy, jak i async/ awaitrozwiązanie problemu piekła zwrotnego, a wybór metody zależy od osobistych preferencji.

Willem van der Veen
źródło
10

Pełne porównanie z zaletami i wadami.

Zwykły JavaScript

  • Plusy
  • Nie wymaga żadnych dodatkowych bibliotek ani technologii
  • Zapewnia najlepszą wydajność
  • Zapewnia najlepszy poziom zgodności z bibliotekami innych firm
  • Umożliwia tworzenie algorytmów ad hoc i bardziej zaawansowanych
  • Cons
  • Może wymagać dodatkowego kodu i stosunkowo złożonych algorytmów

Async (biblioteka)

  • Plusy
  • Upraszcza najpopularniejsze wzorce przepływu sterowania
  • Nadal jest rozwiązaniem opartym na oddzwanianiu
  • Dobry występ
  • Cons
  • Wprowadza zależność zewnętrzną
  • Może nadal nie wystarczyć dla zaawansowanych przepływów

Obietnice

  • Plusy
  • Znacznie upraszcza najbardziej powszechne wzorce przepływu sterowania
  • Solidna obsługa błędów
  • Część specyfikacji ES2015
  • Gwarantuje odroczone wywołanie onFulfilled i onRejected
  • Cons
  • Wymaga interfejsów API promisify opartych na wywołaniach zwrotnych
  • Wprowadza mały hit wydajnościowy

Generatory

  • Plusy
  • Sprawia, że ​​nieblokujący interfejs API wygląda jak blokujący
  • Upraszcza obsługę błędów
  • Część specyfikacji ES2015
  • Cons
  • Wymaga uzupełniającej biblioteki przepływu sterowania
  • Nadal wymaga wywołań zwrotnych lub obietnic implementacji przepływów niesekwencyjnych
  • Wymaga thunkify lub obiecuje interfejsy API nie oparte na generatorze

Async czekaj

  • Plusy
  • Sprawia, że ​​nieblokujący interfejs API wygląda jak blokujący
  • Czysta i intuicyjna składnia
  • Cons
  • Wymaga Babel lub innych transpilerów i pewnej konfiguracji do użycia dzisiaj
Vladimir Bozhinovski
źródło
Skąd to skopiowano? Przynajmniej część z nich znajduje się na stronach 136-137 w książce Node.js Design Patterns (drugie wydanie) (ISBN-10: 1785885588)
Peter Mortensen
6

Async / await może pomóc w uczynieniu kodu czystszym i bardziej czytelnym w przypadkach, gdy potrzebujesz skomplikowanego przepływu sterowania. Tworzy również kod bardziej przyjazny dla debugowania. I umożliwia obsługę błędów synchronicznych i asynchronicznych za pomocą just try/catch.

Niedawno napisałem ten post przedstawiający zalety async / await nad obietnicami w niektórych typowych przypadkach użycia z przykładami kodu: 6 powodów, dla których JavaScript Async / Await Blows Promises Away (Tutorial)

gafi
źródło