web-dev-qa-db-fra.com

Attraper tous les rejets de promesse dans une fonction asynchrone en JavaScript

J'ai rencontré un problème avec la capture de toutes les erreurs lorsque plusieurs promesses génèrent des erreurs de rejet après avoir été attendues dans une fonction asynchrone (javaScript - nœud v8.4.0).

Faites référence au javaScript suivant:

Pour référence, les fonctions timeoutOne () et timeoutTwo () renvoient simplement une promesse native qui résout une valeur après un délai d'expiration de 1 et 2 secondes respectivement, ou rejettent avec une erreur si je mets "deviousState" à true.

let deviousState = true;

async function asyncParallel() {
  try {
    let res1 = timeoutOne();
    let res2 = timeoutTwo();
    console.log(`All done with ${await res1} ${await res2}`)
  }
  catch(err) {
    console.log(err)
  }
}
asyncParallel();

let pAll = Promise.all([timeoutOne(), timeoutTwo()]);
pAll.then((val) => {
  console.log(`All done with ${val[0]} ${val[1]}`)
}).catch(console.log);

Dans les deux cas, seule la promesse qui revient en premier enregistre une erreur. Je sais que dans certaines bibliothèques de promesses, il existe un moyen de consigner toutes les erreurs (par exemple, la méthode "Settle" dans Bluebird), cependant, je ne sais pas s'il existe un analogue de cette méthode dans les promesses natives?

En outre, si les deux promesses sont rejetées, asyncParallel () enregistre une erreur non interceptée avec la promesse qui rejette en dernier. Est-ce parce qu'il n'y a pas de mécanisme intégré pour que les blocs try/catch de la fonction asynchrone capturent plusieurs rejets de cette manière?

Tout fonctionne de la même manière dans les deux cas si les promesses se réalisent. C'est juste que lorsque les deux refusent, Promise.all gère les erreurs, et la version de la fonction asynchrone indique que l'une des erreurs de promesse non gérées plantera le processus dans les futures versions de node.

Existe-t-il de toute façon pour try/catch de gérer correctement ce type d'erreur? Ou dois-je toujours utiliser Promise.all dans les fonctions asynchrones pour m'assurer que les erreurs sont gérées correctement?

8
MFave

Si les deux promesses sont rejetées, asyncParallel() enregistre une erreur non interceptée avec la promesse qui rejette en dernier.

Oui - vous avez créé la promesse timeoutTwo() mais n'avez jamais traité ses erreurs (comme l'utiliser dans un await). await res2 N'a jamais été exécuté en raison de l'exception dans await res1.

(Notez que ce n'est pas "la promesse qui rejette en dernier", mais toujours la promesse qui est attendue en second).

Est-ce parce qu'il n'y a pas de mécanisme intégré pour que les blocs try/catch de la fonction asynchrone capturent plusieurs rejets de cette manière?

Dans le code séquentiel, il ne peut pas y avoir plusieurs exceptions, donc trouver une syntaxe supplémentaire pour les gérer serait difficile.

Dois-je encore utiliser un Promise.all À l'intérieur des fonctions asynchrones pour m'assurer que les erreurs sont gérées correctement?

Oui, exactement ça. Si vous voulez attendre plusieurs promesses en parallèle, vous devez toujours utiliser Promise.all. Le mot clé await n'est que du sucre pour l'appel .then() suivant.

Tu devrais écrire

async function asyncParallel() {
  try {
    let [val1, val2] = await Promise.all([timeoutOne(), timeoutTwo()]);
    console.log(`All done with ${val1} ${val2}`)
  } catch(err) {
    console.log(err)
  }
}

Dans les deux cas, seule la promesse qui revient en premier enregistre une erreur. Je sais que dans certaines bibliothèques de promesses, il existe un moyen de consigner toutes les erreurs (par exemple, la méthode "Settle" dans Bluebird), cependant, je ne sais pas s'il existe un analogue de cette méthode dans les promesses natives?

Non, il n'y en a pas. Les caractéristiques de settle sont triviales à implémenter vous-même en utilisant then, avec toutes les valeurs que vous désirez:

async function asyncParallel() {
  try {
    let [stat1, stat2] = await Promise.all([
        timeoutOne().then(() => "one fulfilled", () => "one rejected"), 
        timeoutTwo().then(() => "two fulfilled", () => "two rejected")
    ]);
    console.log(`All settled with ${stat1} ${stat2}`)
  } catch(err) {
    console.log(err)
  }
}
8
Bergi