Recherche…
Introduction
async
et await
construire sur des promesses et des générateurs pour exprimer des actions asynchrones en ligne. Cela rend le code asynchrone ou de rappel beaucoup plus facile à maintenir.
Les fonctions avec le mot-clé async
renvoient une Promise
et peuvent être appelées avec cette syntaxe.
Dans une async function
le mot-clé await
peut être appliqué à n'importe quelle Promise
et entraînera l'exécution de tout le corps de fonction après l' await
après résolution de la promesse.
Syntaxe
- fonction asynchrone foo () {
...
attend asyncCall ()
} - fonction asynchrone () {...}
- async () => {...}
- (async () => {
const data = attendre asyncCall ()
console.log (data)}) ()
Remarques
Les fonctions asynchrones sont un sucre syntaxique plutôt que des promesses et des générateurs. Ils vous aident à rendre votre code plus lisible, plus facile à entretenir, plus facile à détecter et avec moins d'indentation.
introduction
Une fonction définie comme async
est une fonction qui peut effectuer des actions asynchrones mais reste synchrone. La façon dont il est fait est d' utiliser le await
mot - clé de reporter la fonction alors qu'il attend une promesse de résoudre ou de rejeter.
Remarque: Les fonctions asynchrones constituent une proposition d'étape 4 ("terminée") à inclure dans la norme ECMAScript 2017.
Par exemple, en utilisant l' API Fetch basée sur promesse:
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);
}
}
Une fonction asynchrone renvoie toujours une promesse elle-même, vous pouvez donc l'utiliser dans d'autres fonctions asynchrones.
Style de fonction de flèche
const getJSON = async url => {
const response = await fetch(url);
return await response.json();
}
Moins d'indentation
Avec des promesses:
function doTheThing() {
return doOneThing()
.then(doAnother)
.then(doSomeMore)
.catch(handleErrors)
}
Avec des fonctions asynchrones:
async function doTheThing() {
try {
const one = await doOneThing();
const another = await doAnother(one);
return await doSomeMore(another);
} catch (err) {
handleErrors(err);
}
}
Notez comment le retour est en bas, et pas en haut, et vous utilisez le mécanisme de gestion des erreurs natif du langage ( try/catch
).
Attendre et priorité de l'opérateur
Vous devez garder à l'esprit la priorité de l'opérateur lorsque vous utilisez le mot-clé await
.
Imaginez que nous ayons une fonction asynchrone qui appelle une autre fonction asynchrone, getUnicorn()
qui renvoie une promesse qui se résout en une instance de classe Unicorn
. Maintenant, nous voulons obtenir la taille de la licorne en utilisant la méthode getSize()
de cette classe.
Regardez le code suivant:
async function myAsyncFunction() {
await getUnicorn().getSize();
}
À première vue, cela semble valable, mais ce n'est pas le cas. En raison de la priorité des opérateurs, cela équivaut à ce qui suit:
async function myAsyncFunction() {
await (getUnicorn().getSize());
}
Ici, nous essayons d'appeler la méthode getSize()
de l'objet Promise, ce qui n'est pas ce que nous voulons.
Au lieu de cela, nous devrions utiliser des crochets pour indiquer que nous voulons d'abord attendre la licorne, puis appeler la méthode getSize()
du résultat:
async function asyncFunction() {
(await getUnicorn()).getSize();
}
Bien sûr. La version précédente pourrait être valide dans certains cas, par exemple si la fonction getUnicorn()
était synchrone, mais que la méthode getSize()
était asynchrone.
Fonctions asynchrones par rapport aux promesses
async
fonctions async
ne remplacent pas le type Promise
; ils ajoutent des mots-clés linguistiques qui facilitent les promesses. Ils sont interchangeables:
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) { ... }
Toute fonction qui utilise des chaînes de promesses peut être réécrite en utilisant 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));
}
La fonction peut être réécrite en utilisant async
/ await
comme suit:
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);
}
}
Cette variante async
de newUnicorn()
semble renvoyer une Promise
, mais en réalité, il y avait plusieurs mots clés en await
. Chacun a rendu une Promise
, alors vraiment nous avions une collection de promesses plutôt qu'une chaîne.
En fait , nous pouvons penser en function*
générateur, chaque await
d' être un yield new Promise
. Cependant, les résultats de chaque promesse sont nécessaires pour que la fonction continue. C'est pourquoi le mot-clé async
est nécessaire sur la fonction (ainsi que le mot-clé await
lors de l'appel des promesses) car il indique à Javascript de créer automatiquement un observateur pour cette itération. La Promise
renvoyée par la async function newUnicorn()
résolue à la fin de cette itération.
Pratiquement, vous n'avez pas besoin de considérer cela; await
cache la promesse et async
cache l'itération du générateur.
Vous pouvez appeler des fonctions async
comme si elles étaient des promesses, et await
toute promesse ou toute fonction async
. Vous n'avez pas besoin d' await
une fonction asynchrone, tout comme vous pouvez exécuter une promesse sans .then()
.
Vous pouvez également utiliser un async
IIFE si vous souhaitez exécuter ce code immédiatement:
(async () => {
await makeCoffee()
console.log('coffee is ready!')
})()
En boucle avec async attendent
Lors de l'utilisation de l'attente async dans les boucles, vous pouvez rencontrer certains de ces problèmes.
Si vous essayez simplement d'utiliser forEach
intérieur de forEach
, cela forEach
une erreur de Unexpected token
.
(async() => {
data = [1, 2, 3, 4, 5];
data.forEach(e => {
const i = await somePromiseFn(e);
console.log(i);
});
})();
Cela vient du fait que vous avez vu à tort la fonction de flèche comme un bloc. L' await
sera dans le contexte de la fonction de rappel, qui n'est pas async
.
L'interpréteur nous protège de l'erreur ci-dessus, mais si vous ajoutez async
au rappel forEach
aucune erreur ne sera forEach
. Vous pourriez penser que cela résout le problème, mais cela ne fonctionnera pas comme prévu.
Exemple:
(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');
})();
Cela se produit car la fonction asynchrone de rappel ne peut que suspendre elle-même, pas la fonction asynchrone parente.
Vous pourriez écrire une fonction asyncForEach qui retourne une promesse et vous pourriez alors
await asyncForEach(async (e) => await somePromiseFn(e), data )
quelque chose commeawait asyncForEach(async (e) => await somePromiseFn(e), data )
En gros, vous renvoyez une promesse qui est résolue lorsque tous les callbacks sont attendus. Mais il y a de meilleures façons de le faire, et il suffit d'utiliser une boucle.
Vous pouvez utiliser une boucle for-of
ou une boucle for/while
while, peu importe laquelle vous choisissez.
(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');
})();
Mais il y a une autre prise. Cette solution attendra que chaque appel à somePromiseFn
se termine avant de l'itérer sur le suivant.
C'est génial si vous voulez que vos somePromiseFn
à somePromiseFn
soient exécutés dans l'ordre, mais si vous voulez qu'ils s'exécutent simultanément, vous devrez await
sur 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
reçoit un tableau de promesses comme seul paramètre et renvoie une promesse. Lorsque toutes les promesses du tableau sont résolues, la promesse renvoyée est également résolue. Nous await
cette promesse et quand elle sera résolue, toutes nos valeurs seront disponibles.
Les exemples ci-dessus sont entièrement exécutables. La fonction somePromiseFn
peut être somePromiseFn
comme une fonction d'écho asynchrone avec un délai d'attente. Vous pouvez essayer les exemples dans babel-repl avec au moins le preset stage-3
et regarder la sortie.
function somePromiseFn(n) {
return new Promise((res, rej) => {
setTimeout(() => res(n), 250);
});
}
Opérations asynchrones (parallèles) simultanées
Souvent, vous souhaiterez effectuer des opérations asynchrones en parallèle. Il y a syntaxe directe qui prend en charge ce dans le async
/ await
proposition, mais depuis await
attendra une promesse, vous pouvez envelopper plusieurs promesses ensemble dans Promise.all
à attendre pour eux:
// 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.
}
Cela fera chaque requête pour obtenir les messages de chaque ami en série, mais ils peuvent être faits simultanément:
// 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.
}
Cela fera une boucle sur la liste des identifiants pour créer un tableau de promesses. await
attendra toutes promet d'être complète. Promise.all
combine en une seule promesse, mais elles se font en parallèle.