Pourquoi est -`.catch(err = > console.erreur (err))` découragé?
J'utilise des promesses et j'ai un code qui ressemble à ce qui suit:
function getStuff() {
return fetchStuff().then(stuff =>
process(stuff)
).catch(err => {
console.error(err);
});
}
Ou:
async function getStuff() {
try {
const stuff = await fetchStuff();
return process(stuff);
} catch (err) {
console.error(err);
}
}
Je faisais cela pour éviter de manquer sur les erreurs, mais un autre utilisateur m'a dit que je ne devrais pas faire cela et il est mal vu.
- Quel est le problème avec
return ….catch(err => console.error(err))
? - j'ai vu beaucoup de code qui fait cela, pourquoi?
- Que dois-je faire à la place?
5 réponses
Pourquoi l'ancien code fait-il cela?
Historiquement, les bibliothèques plus anciennes (avant 2013) promettent des rejets de promesses non gérés que vous n'avez pas manipulés vous-même. Cela n'a pas été le cas dans quoi que ce soit écrit depuis lors.
Que se passe-t-il aujourd'hui?
Navigateurs et noeud.js enregistre déjà automatiquement les rejets de promesses non interceptés ou a un comportement pour les gérer et les enregistre automatiquement.
De plus - en ajoutant le .catch
vous signalez à la méthode appelant la fonction qui undefined
est retournée:
// undefined if there was an error
getStuff().then(stuff => console.log(stuff));
La question que l'on devrait se poser lors de l'écriture de code asynchrone est généralement "que ferait la version synchrone du code?":
function calculate() {
try {
const stuff = generateStuff();
return process(stuff);
} catch (err) {
console.error(err);
// now it's clear that this function is 'swallowing' the error.
}
}
Je ne pense pas qu'un consommateur s'attendrait à ce que cette fonction retourne undefined
si une erreur se produit.
Donc, pour résumer les choses - il est mal vu car il surprend les développeurs dans le flux d'application et les navigateurs enregistrent des erreurs de promesse non interceptées de toute façon aujourd'hui.
Que faire au lieu de cela:
Rien., C'est la beauté de celui - ci- si vous avez écrit:
async function getStuff() {
const stuff = await fetchStuff();
return process(stuff);
}
// or without async/await
const getStuff = fetchStuff().then(process);
, En premier lieu, vous obtenez mieux erreurs tout autour de , de toute façon :)
Que faire si j'exécute une ancienne version de Node.js?
Anciennes versions de Nœud.js peut ne pas enregistrer d'erreurs ou afficher un avertissement de dépréciation. Dans ces versions, vous pouvez utiliser console.error
(ou une instrumentation de journalisation appropriée) globalement :
// or throw to stop on errors
process.on('unhandledRejection', e => console.error(e));
Quel est le problème avec
return ….catch(err => console.error(err))
?
Il renvoie une promesse qui remplira avec undefined
après avoir traité l'erreur.
Seul, attraper des erreurs et les enregistrer est bien à la fin d'une chaîne de promesses:
function main() {
const element = document.getElementById("output");
getStuff().then(result => {
element.textContent = result;
}, error => {
element.textContent = "Sorry";
element.classList.add("error");
console.error(error);
});
element.textContent = "Fetching…";
}
Cependant, si getStuff()
attrape l'erreur elle - même pour la consigner et ne fait rien d'autre pour la gérer comme fournir un résultat de secours raisonnable, cela conduit à undefined
apparaître dans la page au lieu de "Désolé".
J'ai vu un beaucoup de code qui fait cela, pourquoi?
Historiquement, les gens avaient peur que les erreurs de promesse ne soient traitées nulle part, ce qui les conduisait à disparaître complètement - à être "avalés" par la promesse. Ils ont donc Ajouté .catch(console.error)
dans chaque fonction pour s'assurer qu'ils remarqueraient des erreurs dans la console.
Ce n'est plus nécessaire car toute implémentation de promesse moderne peut détecter les rejets de promesses non gérés et déclencher des avertissements sur la console.
Bien sûr que c'est toujours nécessaire (ou du moins bonne pratique, même si vous ne vous attendez pas à ce que quelque chose échoue) pour attraper les erreurs à la fin de la chaîne de promesses (lorsque vous ne retournez pas une promesse).
Que dois-je faire à la place?
Dans les fonctions qui return
Une promesse à leur appelant, ne pas enregistrer les erreurs et les avaler en faisant cela. Il suffit de retourner la promesse afin que l'appelant puisse attraper le rejet et gérer l'erreur de manière appropriée (en se connectant ou quoi).
Cela simplifie aussi beaucoup le code:
function getStuff() {
return fetchStuff().then(stuff => process(stuff));
}
async function getStuff() {
const stuff = await fetchStuff();
return process(stuff);
}
Si vous insistez pour faire quelque chose avec la raison de rejet (journalisation, modification des informations), assurez - vous de relancer une erreur:
function getStuff() {
return fetchStuff().then(stuff =>
process(stuff)
).catch(error => {
stuffDetails.log(error);
throw new Error("something happened, see detail log");
});
}
async function getStuff() {
try {
const stuff = await fetchStuff();
return process(stuff);
} catch(error) {
stuffDetails.log(error);
throw new Error("something happened, see detail log");
}
}
Même chose si vous gérez certaines des erreurs:
function getStuff() {
return fetchStuff().then(stuff =>
process(stuff)
).catch(error => {
if (expected(error))
return defaultStuff;
else
throw error;
});
}
async function getStuff() {
try {
const stuff = await fetchStuff();
return process(stuff);
} catch(error) {
if (expected(error))
return defaultStuff;
else
throw error;
}
}
La raison pour laquelle vous ne devriez pas catch
erreurs sauf si absolument nécessaire (ce qui n'est jamais le cas) est que
En plus d'avaler des rejets de promesses, catch handler avale également toutes les erreurs JS cela se produit dans n'importe quel code successif exécuté par le gestionnaire de réussite correspondant.
Implications
Une fois qu'une erreur est détectée par un gestionnaire
catch
, elle est considérée comme effectuée et gérée. Tous les abonnés de promesse successifs dans la chaîne de promesse appelez leurs gestionnaires de succès au lieu de l'échec ou des gestionnaires de capture. Cela conduit à d'étranges comportements. Ce n'est jamais le flux de code prévu.Si une fonction de niveau inférieur comme une méthode de service (
getStuff
) gère les erreurscatch
, il rompt le principe de Séparation des Préoccupations. Une responsabilité du gestionnaire de service devrait être uniquement de récupérer des données. Lorsque cet appel de données échoue, l'application qui appelle ce gestionnaire de service doit gérer erreur.La capture d'erreur dans une fonction attrapée par une autre entraîne des comportements étranges tout autour et rend vraiment difficile le suivi des causes profondes des bogues. Pour suivre de tels BOGUES, Nous devons activer le
Break on Caught Exceptions
dans la console de développement Chrome qui se cassera sur tous lescatch
et pourrait prendre des heures à la fin pour déboguer.
C'est toujours une bonne pratique de gérer les rejets de promesses mais nous devrions toujours le faire en utilisant failure
handler sur catch
handler. Échec le gestionnaire n'attrapera que Promise rejections
et laissera l'application se casser, si une erreur JS se produit, ce qui devrait être le cas.
L'erreur est beaucoup trop générique, c'est un fourre-tout mais il n'y a que tant de choses avec lesquelles l'opération échouerait, l'erreur est tout errorSomethingSpecific donne la granularité
La plupart déclaration générale ici, qui s'applique dans des langues au-delà de javascript, est de ne pas 'catch' une erreur, sauf si vous prévoyez de 'poignée' l'erreur. La journalisation est pas gestion.
C'est-à-dire en général, le meilleur (seulement?) la raison d'une prise est de gérer/ 'traiter' l'erreur dans un constructive façon qui permet au code de continuer sans aucun autre problème. Et encore une fois, une ligne de journalisation n'y parvient probablement jamais...