web-dev-qa-db-fra.com

Pourquoi `.catch (err => console.error (err))` est-il déconseillé?

J'utilise des promesses et j'ai un code qui ressemble à ceci:

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 rater des erreurs, mais un autre utilisateur m'a dit que je ne devrais pas le faire et cela est mal vu.

  • Quel est le problème avec return ….catch(err => console.error(err))?
  • J'ai vu beaucoup de code qui fait ça, pourquoi?
  • Que devrais-je faire à la place?
57
Benjamin Gruenbaum

Pourquoi l'ancien code fait-il cela?

Historiquement, les librairies de promesses non gérées «non gérées» non gérées des bibliothèques (avant 2013) que vous n'avez pas gérées vous-même. Cela n'a pas été le cas dans quoi que ce soit écrit depuis.

Qu'est-ce qui se passe aujourd'hui?

Les navigateurs et Node.js enregistrent déjà automatiquement les rejets de promesses non saisis ou ont un comportement pour les traiter et les enregistrent automatiquement.

De plus, en ajoutant le .catch, vous signalez à la méthode qui appelle la fonction que undefined est renvoyé:

// 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 async 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 renvoie undefined en cas d'erreur.

Donc, pour résumer, cela est mal vu parce que cela surprend les développeurs dans le flux des applications et que les navigateurs enregistrent aujourd'hui des erreurs de promesses non appréhendées.

Que faire à la place:

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 obtiendrez de meilleures erreurs partout de toute façon :)

Que faire si j'utilise une ancienne version de Node.js?

Les anciennes versions de Node.js peuvent ne pas enregistrer les erreurs ou afficher un avertissement de dépréciation. Dans ces versions, vous pouvez utiliser console.error (ou une instrumentation de journalisation appropriée) globally:

// or throw to stop on errors
process.on('unhandledRejection', e => console.error(e));
52
Benjamin Gruenbaum

Quel est le problème avec return ….catch(err => console.error(err))?

Il retourne une promesse qui remplira avec undefined après que vous ayez géré l'erreur.

À lui seul, détecter les erreurs et les consigner s’inscrit parfaitement à la fin de la 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…";
}

Toutefois, si getStuff() intercepte l’erreur elle-même pour la consigner et ne fait rien d’autre pour la gérer, comme si elle fournissait un résultat de repli judicieux, la variable undefined apparaît dans la page au lieu de "Sorry".

J'ai vu beaucoup de code qui fait ça, pourquoi?

Historiquement, les gens avaient peur que les erreurs de promesse ne soient gérées nulle part, ce qui les conduisait à disparaître complètement - étant "avalées" 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 toutes les implémentations modernes de promesses peuvent détecter les rejets de promesses non gérées et déclenchent des avertissements sur la console.

Bien sûr, il est toujours nécessaire (ou au moins une bonne pratique, même si vous ne vous attendez pas à quelque chose d'échouer) de détecter les erreurs à la fin de la chaîne de promesse (lorsque vous ne renvoyez pas plus loin une promesse).

Que devrais-je faire à la place?

Dans les fonctions quireturn constituent une promesse à leur appelant, n'enregistrez pas les erreurs et ne les avalez pas en procédant ainsi. Il suffit de renvoyer la promesse pour que l'appelant puisse capter le rejet et traiter l'erreur de manière appropriée (en se connectant ou autre).

Cela simplifie également 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 le motif de rejet (enregistrement, 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");
  }
}

Idem 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;
  }
}
13
Bergi

La raison pour laquelle vous ne devez pas utiliser les erreurs catch sauf si cela est absolument nécessaire (ce qui n’est jamais) est que 

En plus d’avaler les rejets de promesses, le gestionnaire de captures avale également les erreurs JS cela se produit dans tout code successif exécuté par le gestionnaire de succès correspondant.

Implications

  1. Une fois qu'une erreur est interceptée par un gestionnaire catch, elle est considérée comme effectuée et traitée. Tous les abonnés de promesses successifs de la chaîne de promesses appellent leurs gestionnaires de réussite au lieu de gestionnaires d'échec ou de capture. Cela conduit à des comportements étranges. Ce n'est jamais le flux de code prévu.

  2. Si une fonction de niveau inférieur, telle qu'une méthode de service (getStuff), gère les erreurs dans catch, elle rompt le principe de Séparation des problèmes. La responsabilité du gestionnaire de services devrait être uniquement de récupérer des données. Lorsque cet appel de données échoue, l'application qui appelle ce gestionnaire de services doit gérer l'erreur.

  3. La détection d'erreur dans certaines fonctions capturées par une autre entraîne des comportements étranges et rend très difficile le suivi des causes fondamentales des bogues. Pour suivre de tels bogues, nous devons activer le Break on Caught Exceptions dans la console de développement de Chrome, qui se brisera à chaque catch et pourrait prendre des heures à la fin du débogage.

C'est toujours une bonne pratique de gérer les rejets de promesses, mais nous devrions toujours le faire en utilisant le gestionnaire failure au lieu du gestionnaire catch. Un gestionnaire d'échec ne récupérera que Promise rejections et laissera l'application interrompue, si une erreur JS se produit, ce qui devrait être ainsi.

3
yeshashah

l'erreur est beaucoup trop générique, c'est un piège à tous, mais il y a tellement de choses que l'opération échouerait, l'erreur est tout ce qui est erreur 

2
mmx

La déclaration la plus générale ici, qui s'applique dans les langues autres que javascript, est qu'il ne s'agit pas d'une erreur "catch" sauf si vous envisagez de "gérer" l'erreur. La journalisation est pas manipulation.

c'est-à-dire qu'en général, la meilleure (seule?) raison d'un blocage est de traiter/"traiter" l'erreur de manièreconstructivequi permet au code de continuer sans problème supplémentaire. Et encore une fois, une ligne d’exploitation forestière ne réalise probablement jamais cela ...

0
spechter