Szukaj…


Wprowadzenie

async i await na kompilację na podstawie obietnic i generatorów, aby wyrazić asynchroniczne działania w toku. To znacznie ułatwia utrzymanie kodu asynchronicznego lub kodu zwrotnego.

Funkcje ze słowem kluczowym async zwracają Promise i można je wywoływać przy użyciu tej składni.

Wewnątrz async function słowo kluczowe await można zastosować do dowolnej Promise i spowoduje, że całe ciało funkcji zostanie wykonane po await na wykonanie po spełnieniu obietnicy.

Składnia

  • funkcja asynchroniczna foo () {
    ...
    czekaj na asyncCall ()
    }
  • funkcja asynchroniczna () {...}
  • async () => {...}
  • (async () => {
    const data = czekaj na asyncCall ()
    console.log (dane)}) ()

Uwagi

Funkcje asynchroniczne to cukier składniowy w stosunku do obietnic i generatorów. Pomagają uczynić kod bardziej czytelnym, łatwym do utrzymania, łatwiejszym do wychwycenia błędów i mniejszym poziomem wcięć.

Wprowadzenie

Funkcja zdefiniowana jako async to funkcja, która może wykonywać akcje asynchroniczne, ale nadal wygląda na synchroniczną. Sposób polega na użyciu słowa kluczowego „ await aby odroczyć funkcję na czas oczekiwania na rozstrzygnięcie lub odrzucenie obietnicy .

Uwaga: Funkcje asynchroniczne to propozycja etapu 4 („Zakończona”), która ma zostać uwzględniona w standardzie ECMAScript 2017.

Na przykład za pomocą opartego na obietnicach interfejsu API pobierania Fetch :

async function getJSON(url) {
    try {
        const response = await fetch(url);
        return await response.json();
    }
    catch (err) {
        // Rejections in the promise will get thrown here
        console.error(err.message);
    }
}

Funkcja asynchroniczna zawsze zwraca samą obietnicę, dzięki czemu można jej używać w innych funkcjach asynchronicznych.

Styl funkcji strzałki

const getJSON = async url => {
    const response = await fetch(url);
    return await response.json();
}

Mniej wcięć

Z obietnicami:

function doTheThing() {
    return doOneThing()
        .then(doAnother)
        .then(doSomeMore)
        .catch(handleErrors)
}

Dzięki funkcjom asynchronicznym:

async function doTheThing() {
    try {
        const one = await doOneThing();
        const another = await doAnother(one);
        return await doSomeMore(another);
    } catch (err) {
        handleErrors(err);
    }
}

Zwróć uwagę, jak zwrot jest na dole, a nie na górze, a używasz rodzimej mechaniki obsługi błędów języka ( try/catch ).

Oczekiwanie i pierwszeństwo operatora

Podczas używania słowa kluczowego await należy pamiętać o pierwszeństwie operatora.

Wyobraź sobie, że mamy funkcję asynchroniczną, która wywołuje inną funkcję asynchroniczną, getUnicorn() która zwraca obietnicę, która jest rozwiązywana do wystąpienia klasy Unicorn . Teraz chcemy uzyskać rozmiar jednorożca za pomocą getSize() tej klasy.

Spójrz na następujący kod:

async function myAsyncFunction() {
    await getUnicorn().getSize();
}

Na pierwszy rzut oka wydaje się to ważne, ale tak nie jest. Ze względu na pierwszeństwo operatora odpowiada to:

async function myAsyncFunction() {
    await (getUnicorn().getSize());
}

W tym przypadku próbujemy wywołać metodę getSize() obiektu Promise, co nie jest tym, czego chcemy.

Zamiast tego powinniśmy użyć nawiasów, aby zaznaczyć, że najpierw chcemy poczekać na jednorożca, a następnie wywołać metodę getSize() wyniku:

async function asyncFunction() {
    (await getUnicorn()).getSize();
}

Oczywiście. poprzednia wersja mogła być poprawna w niektórych przypadkach, na przykład, jeśli funkcja getUnicorn() była synchroniczna, ale metoda getSize() była asynchroniczna.

Funkcje asynchroniczne w porównaniu do obietnic

funkcje async nie zastępują typu Promise ; dodają słowa kluczowe w języku, dzięki którym łatwiej jest zadzwonić do obietnic. Są wymienne:

async function doAsyncThing() { ... }

function doPromiseThing(input) { return new Promise((r, x) => ...); }

// Call with promise syntax
doAsyncThing()
    .then(a => doPromiseThing(a))
    .then(b => ...)
    .catch(ex => ...);

// Call with await syntax
try {
    const a = await doAsyncThing();
    const b = await doPromiseThing(a);
    ...
}
catch(ex) { ... }

Każda funkcja korzystająca z łańcuchów obietnic może zostać przepisana przy użyciu funkcji await :

function newUnicorn() {
  return fetch('unicorn.json')                     // fetch unicorn.json from server
  .then(responseCurrent => responseCurrent.json()) // parse the response as JSON
  .then(unicorn =>
    fetch('new/unicorn', {                         // send a request to 'new/unicorn' 
        method: 'post',                            // using the POST method
        body: JSON.stringify({unicorn})            // pass the unicorn to the request body
    })
  )
  .then(responseNew => responseNew.json())
  .then(json => json.success)                      // return success property of response
  .catch(err => console.log('Error creating unicorn:', err));
 }

Funkcję można przepisać za pomocą async / await w następujący sposób:

async function newUnicorn() {
  try {
    const responseCurrent = await fetch('unicorn.json'); // fetch unicorn.json from server
    const unicorn = await responseCurrent.json();        // parse the response as JSON
    const responseNew = await fetch('new/unicorn', {     // send a request to 'new/unicorn'
      method: 'post',                                    // using the POST method
      body: JSON.stringify({unicorn})                    // pass the unicorn to the request body
    });
    const json = await responseNew.json();
    return json.success                                  // return success property of response
  } catch (err) {
    console.log('Error creating unicorn:', err);
  }
}

Ten async wariant newUnicorn() wydaje się zwracać Promise , ale tak naprawdę było wiele await słów kluczowych. Każdy zwrócił Promise , więc naprawdę mieliśmy zbiór obietnic, a nie łańcuch.

W rzeczywistości możemy myśleć o tym jak o generatorze function* , z których każdy await na yield new Promise . Jednak wyniki każdej obietnicy są potrzebne do następnej funkcji. Właśnie dlatego potrzebne jest dodatkowe async słowo kluczowe w funkcji (a także słowo kluczowe await podczas wywoływania obietnic), ponieważ mówi ono Javascriptowi, aby automatycznie tworzył obserwatora dla tej iteracji. Promise zwrócona przez async function newUnicorn() po zakończeniu tej iteracji.

Praktycznie nie musisz tego brać pod uwagę; await ukrywa obietnicę, a async ukrywa iterację generatora.

Możesz wywoływać funkcje async tak, jakby były obietnicami, i await każdą obietnicę lub dowolną funkcję async . Nie musisz await na funkcję asynchroniczną, tak jak możesz wykonać obietnicę bez .then() .

Możesz także użyć async IIFE, jeśli chcesz natychmiast wykonać ten kod:

(async () => {
  await makeCoffee()
  console.log('coffee is ready!')
})()

Pętla z asynchronią czeka

Podczas korzystania z asynchronizacji w pętli możesz napotkać niektóre z tych problemów.

Jeśli po prostu spróbujesz użyć funkcji czekaj wewnątrz forEach , forEach to Unexpected token błąd Unexpected token .

(async() => {
 data = [1, 2, 3, 4, 5];
 data.forEach(e => {
   const i = await somePromiseFn(e);
   console.log(i);
 });
})();

Wynika to z faktu, że błędnie widziałeś funkcję strzałki jako blok. await będzie w kontekście funkcji zwrotnej, która nie jest async .
Interpreter chroni nas przed popełnieniem powyższego błędu, ale jeśli dodasz async do wywołania zwrotnego forEach błędy nie zostaną wyrzucone. Możesz myśleć, że to rozwiązuje problem, ale nie będzie działać zgodnie z oczekiwaniami.

Przykład:

(async() => {
  data = [1, 2, 3, 4, 5];
  data.forEach(async(e) => {
    const i = await somePromiseFn(e);
    console.log(i);
  });
  console.log('this will print first');
})();

Dzieje się tak, ponieważ funkcja asynchroniczna wywołania zwrotnego może wstrzymywać się sama, a nie nadrzędna funkcja asynchroniczna.

Możesz napisać funkcję asyncForEach, która zwraca obietnicę, a następnie możesz await asyncForEach(async (e) => await somePromiseFn(e), data ) coś takiego jak await asyncForEach(async (e) => await somePromiseFn(e), data ) Zasadniczo await asyncForEach(async (e) => await somePromiseFn(e), data ) obietnicę, która jest rozpatrywana, gdy wszystkie połączenia zwrotne są oczekiwane i wykonane. Są jednak lepsze sposoby na zrobienie tego, a mianowicie użycie pętli.

Możesz użyć pętli for-of lub for/while while, tak naprawdę nie ma znaczenia, którą wybierzesz.

(async() => {
  data = [1, 2, 3, 4, 5];
  for (let e of data) {
    const i = await somePromiseFn(e);
    console.log(i);
  }
  console.log('this will print last');
})();

Ale jest jeszcze jeden haczyk. To rozwiązanie będzie somePromiseFn na zakończenie każdego połączenia do somePromiseFn zanim somePromiseFn do następnego.
Jest to świetne, jeśli faktycznie chcesz, aby somePromiseFn wywołania somePromiseFn były wykonywane w kolejności, ale jeśli chcesz, aby działały jednocześnie, musisz await na Promise.all .

(async() => {
 data = [1, 2, 3, 4, 5];
 const p = await Promise.all(data.map(async(e) => await somePromiseFn(e)));
 console.log(...p);
})();

Promise.all otrzymuje tablicę obietnic jako jedyny parametr i zwraca obietnicę. Kiedy wszystkie obietnice w tablicy są rozpatrywane, zwrócona obietnica jest również rozpatrywana. Mamy await na tej obietnicy, a gdy jest to rozwiązane dostępne są wszystkie nasze wartości.

Powyższe przykłady można w pełni uruchomić. Funkcja somePromiseFn może być wykonana jako funkcja echa asynchronicznego z somePromiseFn czasu. Możesz wypróbować przykłady w replice babel z co najmniej ustawieniem wstępnym stage-3 i spojrzeć na wynik.

function somePromiseFn(n) {
 return new Promise((res, rej) => {
   setTimeout(() => res(n), 250);
 });
}

Jednoczesne operacje asynchroniczne (równoległe)

Często będziesz chciał wykonywać operacje asynchroniczne równolegle. Istnieje bezpośrednia składnia, która obsługuje to w propozycji async / await , ale ponieważ await będzie czekać na obietnicę, możesz zapakować wiele obietnic razem w Promise.all aby na nie poczekać:

// Not in parallel

async function getFriendPosts(user) {
    friendIds = await db.get("friends", {user}, {id: 1});
    friendPosts = [];
    for (let id in friendIds) {
        friendPosts = friendPosts.concat( await db.get("posts", {user: id}) );
    }
    // etc.
}

Spowoduje to wykonanie każdego zapytania w celu seryjnego pobrania postów każdego znajomego, ale można to zrobić jednocześnie:

// In parallel

async function getFriendPosts(user) {
    friendIds = await.db.get("friends", {user}, {id: 1});
    friendPosts = await Promise.all( friendIds.map(id => 
      db.get("posts", {user: id})
    );
    // etc.
}

Spowoduje to zapętlenie listy identyfikatorów, tworząc tablicę obietnic. await będzie czekać na wszystkie obietnice być kompletne. Promise.all łączy je w jedną obietnicę, ale są one wykonywane równolegle.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow