wake-up-neo.com

Warum wird `.catch (err => console.error (err))` entmutigt?

Ich verwende Versprechen und habe Code, der wie folgt aussieht:

function getStuff() { 
  return fetchStuff().then(stuff => 
    process(stuff)
  ).catch(err => {
    console.error(err);
  });
}

Oder:

async function getStuff() { 
  try {
    const stuff = await fetchStuff();
    return process(stuff);
  } catch (err) { 
    console.error(err);
  }
}

Ich tat dies, um Fehler zu vermeiden, wurde aber von einem anderen Benutzer gesagt, dass ich dies nicht tun sollte und es ist verpönt.

  • Was ist los mit return ….catch(err => console.error(err))?
  • Ich habe eine Menge Code gesehen, der dies tut, warum?
  • Was soll ich stattdessen tun?
57

Warum macht alter Code das?

In der Vergangenheit haben ältere Versionsbibliotheken (vor 2013) versagt, nicht behandelte Versprechen, die Sie nicht selbst behandelt haben. Dies ist seitdem nicht in etwas geschrieben worden.

Was passiert heute?

Browser und Node.js protokollieren bereits automatisch nicht erfasste Versagensverweigerungen oder Verhaltensweisen für deren Behandlung und protokollieren sie automatisch.

Darüber hinaus signalisieren Sie der Methode, die die Funktion aufruft, indem Sie den .catch hinzufügen, dass undefined zurückgegeben wird:

// undefined if there was an error
getStuff().then(stuff => console.log(stuff)); 

Die Frage, die man sich beim Schreiben von asynchronem Code stellen sollte, lautet normalerweise "Was würde die synchrone Version des Codes tun?":

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.
  }
}

Ich glaube nicht, dass ein Verbraucher erwarten würde, dass diese Funktion undefined zurückgibt, wenn ein Fehler auftritt.

Um es zusammenzufassen - es ist verpönt, weil es Entwickler im Anwendungsfluss überrascht und Browser ohnehin heute ungenutzte Versprechungsfehler protokollieren.

Was ist stattdessen zu tun:

Nichts. Das ist das Schöne daran - wenn Sie geschrieben haben:

async function getStuff() { 
  const stuff = await fetchStuff();
  return process(stuff);
}
// or without async/await
const getStuff = fetchStuff().then(process);

An erster Stelle würden Sie bessere Fehler rund um sowieso :) bekommen.

Was tun, wenn ich eine alte Version von Node.js verwende?

Alte Versionen von Node.js protokollieren möglicherweise keine Fehler oder zeigen eine Warnung vor Verfallsdatum an. In diesen Versionen können Sie console.error (oder geeignete Protokollierungsinstrumente) global verwenden:

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

Was ist los mit return ….catch(err => console.error(err))?

Sie erhalten ein Versprechen, das mit undefined erfüllt wird, nachdem Sie den Fehler behandelt haben.

Das Auffinden von Fehlern und das Protokollieren ist am Ende einer Versprechungskette in Ordnung.

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…";
}

Wenn jedoch getStuff() den Fehler selbst abfängt, um ihn zu protokollieren und nichts anderes als ein vernünftiges Fallback-Ergebnis zu behandeln, führt dies dazu, dass undefined auf der Seite anstelle von "Sorry" angezeigt wird.

Ich habe eine Menge Code gesehen, der dies tut, warum?

In der Vergangenheit hatten die Menschen Angst, dass Versprechungsfehler nirgendwo gehandhabt werden, was dazu führt, dass sie völlig verschwinden - sie werden von dem Versprechen "verschluckt". Sie fügten also in jeder Funktion .catch(console.error) hinzu, um sicherzustellen, dass sie Fehler in der Konsole bemerken würden.

Dies ist nicht mehr erforderlich, da alle modernen Versprechungen die Abweisung unhandlicher Versprechen erkennen und Warnungen auf der Konsole auslösen können.

Natürlich ist es immer noch notwendig (oder zumindest gute Praxis, auch wenn Sie nicht erwarten, dass irgendetwas versagt), Fehler am Ende der Versprechungskette zu fangen (wenn Sie das Versprechen nicht weiter zurückgeben).

Was soll ich stattdessen tun?

In Funktionen, diereturn ein Versprechen an den Anrufer enthalten, protokollieren Sie keine Fehler und verschlucken Sie sie dadurch. Geben Sie einfach das Versprechen zurück, damit der Anrufer die Ablehnung abfangen und den Fehler entsprechend behandeln kann (durch Protokollierung oder etwas anderes).

Dies vereinfacht auch den Code sehr:

function getStuff() { 
  return fetchStuff().then(stuff => process(stuff));
}

async function getStuff() { 
  const stuff = await fetchStuff();
  return process(stuff);
}

Wenn Sie darauf bestehen, etwas mit dem Ablehnungsgrund zu tun (Protokollierung, Änderung von Informationen), stellen Sie sicher, dass ein Fehler erneut ausgegeben wird:

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");
  }
}

Das Gleiche gilt, wenn Sie einige der Fehler behandeln:

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

Der Grund, warum Sie keine catch-Fehler sollten, es sei denn, dies ist absolut erforderlich (was niemals der Fall ist) 

Catch Handler schluckt neben JS-Fehlern auch JS-Fehler Dies tritt in jedem nachfolgenden Code auf, der vom entsprechenden Erfolgshandler ausgeführt wird.

Implikationen

  1. Sobald ein Fehler von einem catch-Handler abgefangen wird, wird er als erledigt betrachtet und behandelt. Alle nachfolgenden Versprechensteilnehmer in der Versprechenkette rufen ihre Erfolgsbearbeiter anstelle von Ausfall- oder Fangbearbeitern auf. Dies führt zu seltsamen Verhaltensweisen. Dies ist niemals der beabsichtigte Codefluss.

  2. Wenn eine Funktion auf einer niedrigeren Ebene wie eine Servicemethode (getStuff) Fehler in catch verarbeitet, wird das Prinzip von Separation of Concerns gebrochen. Der Service-Handler sollte ausschließlich für das Abrufen von Daten verantwortlich sein. Wenn dieser Datenaufruf fehlschlägt, sollte die Anwendung, die den Service-Handler aufruft, den Fehler verwalten.

  3. Das Auffangen von Fehlern in einer Funktion, die von einer anderen Funktion erfasst werden, führt zu seltsamen Verhaltensweisen und macht es sehr schwer, die Ursachen von Fehlern zu finden. Um solche Fehler aufzuspüren, müssen Sie den Break on Caught Exceptions in der Chrome-Dev-Konsole aktivieren, der bei jeder catch abbricht und bei einem Ende des Debuggens Stunden dauern kann.

Es ist immer eine gute Praxis, Versagensversprechen abzuwickeln, aber dies sollte immer mit failure-Handler über catch-Handler erfolgen. Ein Fehlerbehandlungsprogramm fängt nur Promise rejections und lässt die Anwendung unterbrechen, wenn ein JS-Fehler auftritt. Dies ist der Fall.

3
yeshashah

error ist viel zu generisch, es ist alles ein Haken, aber es gibt nur so viele Dinge, mit denen die Operation fehlschlagen würde 

2
mmx

Die allgemeinste Aussage, die in Sprachen jenseits von Javascript gilt, ist nicht 'catch' ein Fehler, es sei denn, Sie beabsichtigen, 'handle' den Fehler. Protokollierung ist nicht Handhabung.

in der Regel besteht der beste (einzige?) Grund für einen Catch darin, den Fehler auf einekonstruktive- Weise zu behandeln, mit der der Code ohne weitere Probleme ausgeführt werden kann. Und wieder erreicht eine Protokollierung wahrscheinlich nie das ...

0
spechter