©Sergey Emelyanov 2025 | Alle Rechte vorbehalten
Das Thema der Fehlerbehandlung in Javascript kommt fast jedem in den Sinn. Ich sollte anmerken, dass zu diesem Thema schon eine ganze Menge Artikel geschrieben wurden. Deshalb werde ich nicht auf Details und Grundlagen eingehen. Ich werde meine eigene Variante der Fehlerbehandlung anbieten.
Zu Beginn möchte ich Sie daran erinnern, dass ein Fehler in Javascript im Wesentlichen das „Auslösen einer Ausnahme“ ist. Später muss die Ausnahme von der Anwendung behandelt werden. Standardmäßig wird eine Ausnahme durch das Error-Objekt ausgelöst.
Der traditionelle try/catch-Ansatz hat seine Nachteile: fehlende strenge Typisierung im catch-Block, Vermischung von technischen Fehlern und Fehlern der Geschäftslogik und häufige Fälle, in denen Entwickler vergessen, Fehler an kritischen Stellen zu behandeln. Dieses Problem kann durch die Verwendung des Either-Musters gelöst werden, das eine explizite Trennung zwischen erfolgreichen und fehlerhaften Ergebnissen ermöglicht. In diesem Artikel wird beschrieben, wie die Fehlerbehandlung in TypeScript durch das Erstellen benutzerdefinierter Ausnahmen implementiert wird, und es wird gezeigt, wie die Monet-Bibliothek verwendet wird, um einen funktionalen Ansatz für die Fehlerbehandlung zu wählen.
Die übliche Art, Fehler zu behandeln, ist die Verwendung von try/catch-Blöcken. Dem catch-Block fehlt jedoch die Fehlertypisierung (der Standardtyp ist any), was zu Schwierigkeiten bei der weiteren Analyse der Ausnahme führen kann. Außerdem ist es schwierig, technische Fehler (z. B. Datenbankverbindungsfehler) von Fehlern in der Geschäftslogik zu trennen.
Um dieses Problem zu lösen, werden oft benutzerdefinierte Fehler geschrieben. Hier ist ein Beispiel für die Erstellung eines benutzerdefinierten Fehlers:
class OrderError extends Error {
constructor(code: string, message: string) {
super(message);
this.name = "OrderError";
this.code = code; // insufficient_funds, invalid_payment, incorrect_id etc
const codeMessageMap = {
'insufficient_funds': 'You do not have enough funds to complete this order',
'invalid_payment': 'There was an error during last payment',
'incorrect_id': 'Wrong ID was provided',
}
this.userMessage = codeMessageMap[code] || "An error occurred with your order"
}
}
function processOrder(orderId: number): void {
if (orderId <= 0) {
throw new OrderError("incorrect_id", "Wrong ID");
}
}
Mit diesem Ansatz können Sie den Fehlertyp expliziter definieren, aber der catch-Block selbst arbeitet weiterhin mit dem Typ any, was nicht immer praktisch ist.
So wird die Fehlerbehandlung in diesem Fall aussehen:
try{
processOrder(2)
}catch(err){
if(err instanceof OrderError){
if(err.code === 'incorrect_id'){
alert(err.userMessage)
console.log(err.message);
}
if(err.code === 'invalid_payment'){
// show message to user
}
} else{
alert('Something went wrong! Please contact support')
console.log(err.message);
}
}
Javascript-Fehler unterstützen auch die Verschachtelung. Im folgenden Beispiel fangen wir einen Fehler ab, verpacken ihn in unseren eigenen, wobei der Kontext erhalten bleibt und eine klarere Beschreibung hinzugefügt wird.
function displayMoney(cents){
try{
document.querySelector("#money").innerText = formatMoney(cents)
}catch(err){
if(err.message.includes('innerText')){
throw new Error('Div with the id of money is missing from the page', {
cause: err
})
}
throw err;
}
}
Es ist oft schwierig, technische Fehler von Fehlern in der Geschäftslogik zu unterscheiden. Betrachten wir das folgende Beispiel, bei dem nach der Verarbeitung einer API-Anforderung ein Fehler auftritt, so können wir auf der darüber liegenden Ebene nicht genau erkennen, ob das Problem technischer oder geschäftslogischer Natur ist.
async function fetchWithRetry(url, attempts = 3){
while(attempts > 0){
try{
const res = await fetch(url)
if(res.ok){
return res
}else{
throw new Error("Network response was not ok")
}
}catch(err){
attempts--;
if(attempts === 0){
throw err
}
}
}
}
try{
await fetchWithRetry("https://httpbin.org/status/200,500")
}catch(err){
console.log("caught retry example", err)
}
console.log("end!")
Das Entweder-Muster ist in der funktionalen Programmierung weit verbreitet. Es besteht im Wesentlichen darin, dass eine Funktion keine Ausnahme, sondern ein Ergebnis zurückgibt, das entweder erfolgreich (rechts) oder fehlerhaft (links) sein kann. Dieser Ansatz ähnelt der Fehlerbehandlung in Go, bei der Fehler zusammen mit dem Ergebnis zurückgegeben werden, anstatt weggeworfen zu werden.
Vorteile von Either:
In diesem Beispiel implementieren wir die Funktion zum Aktualisieren eines E-Mail-Clients in CRM. Wir verwenden die Monet-Bibliothek, um mit Either zu arbeiten. Wenn die Aktualisierung feststellt, dass der Client nicht existiert oder eine ungültige E-Mail übergeben wird, gibt die Funktion Either.left mit dem entsprechenden Fehler zurück. Andernfalls - Either.right mit den aktualisierten Kundendaten.
Code-Beispiel:
import { Either } from "monet";
interface Client {
id: number;
name: string;
email: string;
}
class NotFoundError extends Error {
constructor(code: int, message: string) {
super(message);
this.name = "NotFoundError";
this.code = code
}
}
class ValidationError extends Error {
constructor(field: string, message: string) {
super(message);
this.name = "BusinessError";
this.field = field;
}
}
const fakeDatabase: Client[] = [
{ id: 1, name: "Иван Иванов", email: "ivan@example.com" },
{ id: 2, name: "Мария Петрова", email: "maria@example.com" }
];
function updateClientEmail(clientId: number, newEmail: string): Either<Error, Client> {
const client: Client | undefined = fakeDatabase.find(client => client.id === clientId);
if (!client) {
return Either.Left(new NotFoundError(404, `Клиент с id=${clientId} не найден`));
}
if (!newEmail.includes("@")) {
return Either.Left(new BusinessError("email", "Wrong email provided"));
}
client.email = newEmail;
return Either.Right(client);
}
// How to use it
const result = updateClientEmail(1, "newemail@example.com");
result.cata(
(error) => console.error("Ошибка при обновлении клиента:", error.message),
(client) => console.log("Клиент успешно обновлен:", client)
);
In diesem Beispiel:
Wie Either funktioniert:
Weitere Bereiche für Verbesserungen:
Die Fehlerbehandlung ist ein wichtiger Aspekt bei der Entwicklung stabiler und skalierbarer CRM-Systeme. Traditionelles try/catch bietet nicht immer die notwendige Typisierung und Trennung von Fehlern, was zu Komplexität bei der Codewartung führen kann. Die Verwendung des Either-Patterns mit der Monet-Bibliothek zeigt, wie die Fehlerbehandlung deklarativ, leicht komponentisierbar und robust gestaltet werden kann. Das obige Beispiel zeigt, wie eine Funktion zur Aktualisierung von Client-Daten implementiert werden kann, indem erfolgreiche Ergebnisse und Fehlersituationen explizit voneinander getrennt werden. Dieser funktionale Ansatz vereinfacht die Skalierung und Integration von Systemkomponenten und macht den Code transparenter und überschaubarer.
©Sergey Emelyanov 2025 | Alle Rechte vorbehalten