Disons que j'ai un ensemble de Promise
s qui font des requêtes réseau, dont l'un échouera:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Disons que je veux attendre jusqu'à ce que tout cela soit terminé, peu importe si l'un a échoué. Il peut y avoir une erreur de réseau pour une ressource dont je peux vivre sans, mais que si je peux obtenir, je veux avant de continuer. Je veux gérer les pannes de réseau avec élégance.
Comme Promises.all
cela ne laisse aucune place à cela, quel est le modèle recommandé pour gérer cela, sans utiliser une bibliothèque de promesses?
javascript
promise
es6-promise
Nathan Hagen
la source
la source
allSettled
qui répond à vos besoins sans que vous ayez à rouler le vôtre.Promise.all
rejettera dès que toute promesse sera rejetée, donc votre idiome proposé ne garantit pas que toutes les promesses sont réglées.Réponses:
Mise à jour, vous souhaitez probablement utiliser le natif intégré
Promise.allSettled
:Comme un fait amusant, cette réponse ci-dessous était l'art antérieur en ajoutant cette méthode à la langue:]
Bien sûr, vous avez juste besoin d'un
reflect
:Ou avec ES5:
Ou dans votre exemple:
la source
reflect
un mot courant en informatique? Pouvez-vous s'il vous plaît lien vers où cela est expliqué comme sur wikipedia ou quelque chose. Je cherchais durPromise.all not even first reject
mais je ne savais pas comment chercher "Reflect". ES6 devrait-il avoir unPromise.reflect
qui est comme "Promise.all mais vraiment tout"?Réponse similaire, mais plus idiomatique pour ES6 peut-être:
Selon le (s) type (s) de valeurs renvoyées, les erreurs peuvent souvent être distinguées assez facilement (par exemple, utiliser
undefined
pour "indifférent",typeof
pour des valeurs non-objet simplesresult.message
,result.toString().startsWith("Error:")
etc.)la source
.map(p => p.catch(e => e))
résolues ou rejetées n'ont pas d'importance ici car la pièce transforme tous les rejets en valeurs résolues, doncPromise.all
attend toujours que tout se termine, que les fonctions individuelles soient résolues ou rejetées, quel que soit le temps qu'elles prennent. Essayez-le..catch(e => console.log(e));
n'est jamais appelé parce que cela n'échoue jamaiscatch
est généralement une bonne pratique à mon humble avis .e
et la renvoie en tant que valeur régulière (succès). Identique àp.catch(function(e) { return e; })
seulement plus court.return
est implicite.La réponse de Benjamin offre une excellente abstraction pour résoudre ce problème, mais j'espérais une solution moins abstraite. La manière explicite de résoudre ce problème consiste simplement à faire appel
.catch
aux promesses internes et à renvoyer l'erreur à partir de leur rappel.En allant plus loin, vous pouvez écrire un gestionnaire de capture générique qui ressemble à ceci:
alors tu peux faire
Le problème avec cela est que les valeurs capturées auront une interface différente de celle des valeurs non capturées, donc pour nettoyer cela, vous pourriez faire quelque chose comme:
Alors maintenant, vous pouvez le faire:
Ensuite, pour le garder au sec, vous obtenez la réponse de Benjamin:
où il ressemble maintenant
Les avantages de la deuxième solution sont qu'elle est abstraite et sèche. L'inconvénient est que vous avez plus de code, et vous devez vous rappeler de refléter toutes vos promesses pour rendre les choses cohérentes.
Je caractériserais ma solution comme explicite et KISS, mais en effet moins robuste. L'interface ne garantit pas que vous savez exactement si la promesse a réussi ou échoué.
Par exemple, vous pourriez avoir ceci:
Cela ne sera pas rattrapé
a.catch
, alorsIl n'y a aucun moyen de dire lequel a été fatal et lequel ne l'a pas été. Si c'est important, vous allez vouloir appliquer et une interface qui suit si elle a réussi ou non (ce qui est le
reflect
cas).Si vous souhaitez simplement gérer les erreurs avec élégance, vous pouvez simplement traiter les erreurs comme des valeurs non définies:
Dans mon cas, je n'ai pas besoin de connaître l'erreur ou comment elle a échoué - je me soucie simplement si j'ai la valeur ou non. Je laisse la fonction qui génère la promesse s'inquiéter de la journalisation de l'erreur spécifique.
De cette façon, le reste de l'application peut ignorer son erreur s'il le souhaite et la traiter comme une valeur indéfinie s'il le souhaite.
Je veux que mes fonctions de haut niveau échouent en toute sécurité et ne se soucient pas des détails sur les raisons de l'échec de ses dépendances, et je préfère également KISS à DRY lorsque je dois faire ce compromis - c'est pourquoi j'ai finalement choisi de ne pas utiliser
reflect
.la source
Promise
s. Tandis que votrereflect
améliore la réutilisation du code, il établit également un autre niveau d'abstraction. Étant donné que la réponse de Nathan n'a jusqu'à présent reçu qu'une fraction des votes positifs par rapport à la vôtre, je me demande si cela indique un problème avec sa solution, que je n'ai pas encore reconnu.new Promise((res, rej) => res(new Error('Legitimate error'))
ne se distinguerait-il pasnew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Ou plus loin, vous ne pourriez pas filtrerx.status
? J'ajouterai ce point à ma réponse pour que la différence soit plus clairePromise.all()
variante spécifique , il incombe également au consommateur de Promise de savoir qu'une promesse spécifique ne rejettera pas mais sera avaler ce sont des erreurs. En fait, lareflect()
méthode pourrait être rendue moins «abstraite» et plus explicite en l'appelant.PromiseEvery(promises).then(...)
La complexité de la réponse ci-dessus par rapport à celle de Benjamin devrait en dire long sur cette solution.Il y a une proposition finalisée pour une fonction qui peut accomplir cela nativement, en vanilla Javascript
Promise.allSettled
:, qui a atteint l'étape 4, est officialisé dans ES2020, et est implémenté dans tous les environnements modernes . Elle est très similaire à lareflect
fonction de cette autre réponse . Voici un exemple, de la page de proposition. Avant, vous auriez dû faire:En utilisant à la
Promise.allSettled
place, ce qui précède sera équivalent à:Ceux qui utilisent des environnements modernes pourront utiliser cette méthode sans aucune bibliothèque . Dans ces cas, l'extrait de code suivant devrait s'exécuter sans problème:
Production:
Pour les navigateurs plus anciens, il y a un polyfill conforme spécifications ici .
la source
J'aime vraiment la réponse de Benjamin, et comment il transforme fondamentalement toutes les promesses en engagements toujours résolus mais parfois avec erreur. :)
Voici ma tentative à votre demande au cas où vous cherchiez des alternatives. Cette méthode traite simplement les erreurs comme des résultats valides et est codée de la même
Promise.all
manière:la source
settle
. Nous avons cela aussi dans bluebird, j'aime mieux réfléchir mais c'est une solution viable pour quand vous avez ceci pour un tableau.Promise
constructeur correctement (et éviter cevar resolve
truc)?Le
Promise.all
va avaler toute promesse rejetée et stocker l'erreur dans une variable, il reviendra donc lorsque toutes les promesses auront été résolues. Ensuite, vous pouvez renvoyer l'erreur ou faire n'importe quoi. De cette façon, je suppose que vous obtiendrez le dernier rejet au lieu du premier.la source
err.push(error)
les erreurs en en faisant un tableau et en l'utilisant , de sorte que toutes les erreurs puissent être propagées.J'ai eu le même problème et je l'ai résolu de la manière suivante:
Dans ce cas ,
Promise.all
vous attendra chaque promesse entrera enresolved
ourejected
état.Et avec cette solution, nous «arrêtons l'
catch
exécution» de manière non bloquante. En fait, nous n'arrêtons rien, nous retournons simplement lePromise
dans un état en attente qui en retourne un autrePromise
lorsqu'il est résolu après le délai d'expiration.la source
Promise.all
. Je cherche un moyen d'écouter quand toutes les promesses ont été invoquées, mais pas de les invoquer moi-même. Merci.all()
fait cela, elle attend la réalisation de toutes les promesses ou le rejet d'au moins une d'entre elles..all
tout se déclenche.then
ou un.all
appel) mais qu'elles s'exécutaient lorsqu'elles étaient créées.Cela devrait être cohérent avec la façon dont Q le fait :
la source
La réponse de Benjamin Gruenbaum est bien sûr excellente. Mais je peux aussi voir si le point de vue de Nathan Hagen avec le niveau d'abstraction semble vague. Avoir des propriétés d'objet courtes comme
e & v
n'aide pas non plus, mais bien sûr, cela pourrait être changé.En Javascript, il existe un objet Error standard, appelé
Error
,. Idéalement, vous jetez toujours une instance / un descendant de cela. L'avantage est que vous pouvez le faireinstanceof Error
, et vous savez que quelque chose est une erreur.Donc, en utilisant cette idée, voici mon point de vue sur le problème.
En gros, intercepter l'erreur, si l'erreur n'est pas de type Erreur, encapsuler l'erreur dans un objet Error. Le tableau résultant aura soit des valeurs résolues, soit des objets d'erreur que vous pourrez vérifier.
L'instance de l'intérieur de la capture, est au cas où vous utilisez une bibliothèque externe qui pourrait le faire
reject("error")
, au lieu dereject(new Error("error"))
.Bien sûr, vous pourriez avoir des promesses si vous résolviez une erreur, mais dans ce cas, il serait très logique de traiter de toute façon une erreur, comme le montre le dernier exemple.
Un autre avantage de le faire, la destruction de tableaux reste simple.
Au lieu de
Vous pourriez faire valoir que la
!error1
vérification est plus simple qu'une instance de, mais vous devez également détruire les deuxv & e
.la source
Au lieu de rejeter, résolvez-le avec un objet. Vous pouvez faire quelque chose comme ça lorsque vous mettez en œuvre la promesse
la source
Je pense que ce qui suit offre une approche légèrement différente ... comparer
fn_fast_fail()
avecfn_slow_fail()
... bien que ce dernier n'échoue pas en tant que tel ... vous pouvez vérifier si l'un ou les deuxa
etb
est une instance deError
etthrow
queError
si vous voulez qu'il atteigne lecatch
bloc (par exempleif (b instanceof Error) { throw b; }
). Voir le jsfiddle .la source
Voici ma coutume
settledPromiseAll()
Par rapport à
Promise.all
Si toutes les promesses sont résolues, il fonctionne exactement comme celui standard.
Si une ou plusieurs promesses sont rejetées, elle renvoie la première rejetée de la même manière que la norme, mais contrairement à elle, elle attend que toutes les promesses soient résolues / rejetées.
Pour les courageux, nous pourrions changer
Promise.all()
:ATTENTION . En général, nous ne modifions jamais les fonctionnalités intégrées, car cela pourrait casser d'autres bibliothèques JS non liées ou se heurter à de futures modifications des normes JS.
My
settledPromiseall
est rétrocompatible avecPromise.all
et étend ses fonctionnalités.Les personnes qui développent des normes - pourquoi ne pas les inclure dans une nouvelle norme Promise?
la source
Promise.all
avec uneasync/await
approche modernela source
Je ferais:
la source
Vous pouvez exécuter votre logique séquentiellement via l'exécuteur synchrone nsynjs . Il s'arrêtera sur chaque promesse, attendra la résolution / rejet et affectera le résultat de la résolution à la
data
propriété ou lèvera une exception (pour la gestion dont vous aurez besoin du bloc try / catch). Voici un exemple:la source
J'utilise les codes suivants depuis ES5.
La signature d'utilisation est exactement comme
Promise.all
. La principale différence est qu'ilPromise.wait
attendra toutes les promesses pour terminer son travail.la source
Je sais que cette question a beaucoup de réponses, et je suis sûr qu'elle doit (sinon toutes) être correcte. Cependant, il m'a été très difficile de comprendre la logique / le flux de ces réponses.
J'ai donc regardé la mise en œuvre d'origine
Promise.all()
et j'ai essayé d'imiter cette logique - à l'exception de ne pas arrêter l'exécution si une promesse échouait.Explication:
- Faites une boucle sur l'entrée
promisesList
et exécutez chaque promesse.- Peu importe si la promesse a été résolue ou rejetée: enregistrez le résultat de la promesse dans un
result
tableau selon leindex
. Enregistrez également le statut de résolution / rejet (isSuccess
).- Une fois toutes les promesses terminées, renvoyez une promesse avec le résultat de toutes les autres.
Exemple d'utilisation:
la source
Promise.all
vous réimplémenter, il y a trop de choses qui vont mal se passer. Votre version ne gère pas les entrées vides par exemple.Je ne sais pas quelle bibliothèque de promesses vous utilisez, mais la plupart ont quelque chose comme allSettled .
Edit: Ok puisque vous voulez utiliser ES6 ordinaire sans bibliothèques externes, il n'y a pas une telle méthode.
En d'autres termes: vous devez parcourir vos promesses manuellement et résoudre une nouvelle promesse combinée dès que toutes les promesses sont réglées.
la source