Ein verstecktes Juwel beim JavaScript-Debugging: error.cause
Grace Collins
Solutions Engineer · Leapcell

Die Herausforderungen beim Debuggen
Was ist die größte Herausforderung beim Debuggen? Eine davon ist zweifellos das Auffinden der Fehlerquelle.
Stellen Sie sich folgendes Szenario vor:
const func = () => { doSth('A'); doSth('B'); };
Wenn func
einen Fehler auslöst, wie identifizieren Sie, bei welchem Schritt der Fehler aufgetreten ist? Wurde er durch doSth('A')
, doSth('B')
oder func
selbst verursacht? Offensichtlich fehlt dem Fehler ausreichend Kontext.
Häufige Lösungen
Ein üblicher Ansatz zur Behebung dieses Problems könnte wie folgt aussehen:
const func = () => { try { doSth('A'); } catch (error) { throw new Error('Ein Fehler von A', error); } try { doSth('B'); } catch (error) { throw new Error('Ein Fehler von B', error); } };
Mit diesem Ansatz können Sie die Fehlerquelle leichter lokalisieren. Diese Lösung hat jedoch mehrere Einschränkungen:
-
Verlust von Fehlerdetails: Wenn der Fehler umfangreiche Informationen enthält (z. B. Nutzlasten, HTTP-Statuscodes, Fehlercodes), fügt dieser Ansatz nur die Fehlermeldung von
doSth
zum neu erstellten Fehler hinzu. Andere wichtige Details, einschließlich des ursprünglichen Stacktrace, gehen verloren. -
Verringerte Lesbarkeit der Protokolle: Bei mehr als zwei potenziellen Fehlerpunkten können die Protokolle unübersichtlich und schwer zu interpretieren sein.
-
Uneindeutigkeit bei der Ausdrucksweise: Der Code kommuniziert nicht explizit, dass der neue Fehler durch die spezifische abgefangene
doSth
-Funktion verursacht wird, was Raum für eine verbesserte Lesbarkeit des Codes lässt.
Einführung von error.cause
Um diese Probleme zu beheben, führte ECMAScript 2022 error.cause
ein.
Diese Funktion ermöglicht es Entwicklern, die Ursache eines Fehlers anzugeben, wenn ein neues Fehlerobjekt erstellt wird. Durch die Verwendung von error.cause
können Sie eine Kette von Fehlern erstellen, die es einfacher macht, die Ursache eines Problems zu debuggen und zurückzuverfolgen.
Hier ist ein einfaches Beispiel:
try { // Some operation that may throw an error } catch (error) { throw new Error('Something went wrong', { cause: error }); }
Mit diesem Ansatz können Sie kausale Verbindungen zwischen Fehlern aufbauen. Zum Beispiel:
const func = () => { try { doSth('A'); } catch (error) { throw new Error('Ein Fehler von A', { cause: error }); } try { doSth('B'); } catch (error) { throw new Error('Ein Fehler von B', { cause: error }); } };
Dies ermöglicht es uns, Fehler abzufangen, die von Funktionen niedrigerer Ebene ausgelöst werden (z. B. doSth('A')
), einen neuen Fehler auszulösen, der relevanten Kontext hinzufügt (z. B. "Ein Fehler ist bei der Ausführung von doSth('A')
aufgetreten"), und die ursprünglichen Fehlerdetails beizubehalten (z. B. "A ist ein unzulässiges Argument.").
Aufbau einer Fehlerkette
Ein weiterer Vorteil von error.cause
ist die Möglichkeit, eine Kette verknüpfter Fehler zu erstellen, die es Entwicklern ermöglicht, Probleme durch mehrere Schichten der Anwendung zurückzuverfolgen:
const func = () => { try { try { try { doSth('A'); } catch (error) { throw new Error('Error at depth 3', { cause: error }); } } catch (error) { throw new Error('Error at depth 2', { cause: error }); } } catch (error) { throw new Error('Error at depth 1', { cause: error }); } }; console.log(error.cause.cause); // Error at depth 3
In Node.js werden Fehler mit einer cause
in der Konsole speziell behandelt. Alle zugehörigen Fehler-Stacks werden ausgegeben:
const cause = new Error('The remote HTTP server responded with a 500 status'); const symptom = new Error('The message failed to send', { cause }); console.log(symptom); // Prints: // Error: The message failed to send // at REPL2:1:17 // at Script.runInThisContext (node:vm:130:12) // ... 7 lines matching cause stack trace ... // at [_line] [as _line] (node:internal/readline/interface:886:18) { // [cause]: Error: The remote HTTP server responded with a 500 status // at REPL1:1:15 // at Script.runInThisContext (node:vm:130:12) // at REPLServer.defaultEval (node:repl:574:29) // at bound (node:domain:426:15) // at REPLServer.runBound [as eval] (node:domain:437:12) // at REPLServer.onLine (node:repl:902:10) // at REPLServer.emit (node:events:549:35) // at REPLServer.emit (node:domain:482:12) // at [_onLine] [as _onLine] (node:internal/readline/interface:425:12) // at [_line] [as _line] (node:internal/readline/interface:886:18)
Schlussfolgerung
- Das Debuggen wird deutlich einfacher, wenn Sie sofortigen Zugriff auf den Fehlerkontext und die Details haben.
- Eine effektive Möglichkeit, dies zu erreichen, ist die Einführung eines "catch + rethrow with context"-Musters mit der Funktion
error.cause
:
try { doSth(); } catch (error) { throw new Error('Context relative to doSth', { cause: error }); }
Dieser Ansatz verbessert nicht nur die Rückverfolgbarkeit von Fehlern, sondern auch die Lesbarkeit und Wartbarkeit Ihres Codes.
Wir sind Leapcell, Ihre erste Wahl für die Bereitstellung von Node.js-Projekten in der Cloud.
Leapcell ist die Next-Gen Serverless Plattform für Web Hosting, Async Tasks und Redis:
Multi-Language Support
- Entwickeln Sie mit Node.js, Python, Go oder Rust.
Stellen Sie unbegrenzt Projekte kostenlos bereit
- Zahlen Sie nur für die Nutzung – keine Anfragen, keine Gebühren.
Unschlagbare Kosteneffizienz
- Pay-as-you-go ohne Leerlaufgebühren.
- Beispiel: 25 US-Dollar unterstützen 6,94 Millionen Anfragen bei einer durchschnittlichen Antwortzeit von 60 ms.
Optimierte Entwicklererfahrung
- Intuitive Benutzeroberfläche für mühelose Einrichtung.
- Vollautomatische CI/CD-Pipelines und GitOps-Integration.
- Echtzeit-Metriken und -Protokollierung für umsetzbare Erkenntnisse.
Mühelose Skalierbarkeit und hohe Leistung
- Automatische Skalierung zur einfachen Bewältigung hoher Parallelität.
- Null Betriebsaufwand – konzentrieren Sie sich einfach auf das Erstellen.
Erfahren Sie mehr in der Dokumentation!
Folgen Sie uns auf X: @LeapcellHQ