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 jakawait asyncForEach(async (e) => await somePromiseFn(e), data )
Zasadniczoawait 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.