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 comme await 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.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow