Usamos cookies para mejorar tu experiencia en el sitio
CodeWorlds

Własne klasy błędów

Witaj ponownie w Parku Jurajskim! W poprzednich ćwiczeniach poznaliśmy różne rodzaje błędów występujących w JavaScript oraz podstawy ich obsługi za pomocą

try...catch...finally
. Teraz zagłębimy się w tworzenie własnych, niestandardowych klas błędów, które są nieocenione w dużych aplikacjach - takich jak systemy zarządzania parkiem pełnym prehistorycznych gadów.

Dlaczego własne klasy błędów?

W złożonych systemach, takich jak Park Jurajski, potrzebujemy precyzyjnej kategoryzacji problemów. Nie wystarczy wiedzieć, że "coś poszło nie tak" - musimy wiedzieć dokładnie, co się stało, gdzie, i jak na to zareagować.

Własne klasy błędów pozwalają na:

  1. Precyzyjne kategoryzowanie różnych typów problemów
  2. Dołączanie dodatkowych danych kontekstowych do błędów
  3. Implementację własnych metod pomocniczych
  4. Łatwiejsze filtrowanie i obsługę specyficznych przypadków

To jak różnica między ogólnym alarmem w parku a precyzyjnym komunikatem: "Velociraptory wydostały się z wybiegu B6, ostatnio widziane zmierzające w kierunku centrum dla zwiedzających".

Podstawy tworzenia własnych klas błędów

W JavaScript wszystkie błędy dziedziczą z bazowej klasy

Error
. Nasze własne klasy błędów powinny również dziedziczyć z
Error
lub jednej z jej podklas.

Prosty przykład własnej klasy błędu

1class DinosaurError extends Error {
2  constructor(message) {
3    super(message); // Wywołanie konstruktora klasy nadrzędnej
4    this.name = 'DinosaurError'; // Nadpisanie nazwy błędu
5  }
6}
7
8// Użycie
9try {
10  throw new DinosaurError('Nieoczekiwane zachowanie T-Rexa');
11} catch (error) {
12  console.log(error.name); // 'DinosaurError'
13  console.log(error.message); // 'Nieoczekiwane zachowanie T-Rexa'
14}

Dodawanie dodatkowych właściwości do błędów

Największą zaletą własnych klas błędów jest możliwość dodania dodatkowych właściwości z kontekstem:

1class EnclosureBreachError extends Error {
2  constructor(message, dinosaurSpecies, location, dangerLevel) {
3    super(message);
4    this.name = 'EnclosureBreachError';
5    this.dinosaurSpecies = dinosaurSpecies;
6    this.location = location;
7    this.dangerLevel = dangerLevel;
8    this.timestamp = new Date();
9  }
10
11  getEmergencyMessage() {
12    return `UWAGA! ${this.dinosaurSpecies} wydostał się z wybiegu w lokalizacji ${this.location}.
13    Poziom zagrożenia: ${this.dangerLevel}/10.
14    Czas zdarzenia: ${this.timestamp.toLocaleTimeString()}`;
15  }
16}
17
18// Użycie
19try {
20  throw new EnclosureBreachError(
21    'Naruszenie zabezpieczeń wybiegu',
22    'Velociraptor',
23    'Sektor B6',
24    9
25  );
26} catch (error) {
27  if (error instanceof EnclosureBreachError) {
28    console.error(error.getEmergencyMessage());
29
30    // Uruchomienie odpowiednich procedur awaryjnych zależnie od poziomu zagrożenia
31    if (error.dangerLevel >= 8) {
32      evacuateSector(error.location);
33      dispatchSecurityTeam(error.location, error.dinosaurSpecies);
34    }
35  }
36}

Hierarchia klas błędów

W dużych aplikacjach, takich jak system zarządzania Parkiem Jurajskim, warto stworzyć hierarchię klas błędów:

1// Bazowa klasa błędu specyficzna dla aplikacji
2class JurassicParkError extends Error {
3  constructor(message) {
4    super(message);
5    this.name = 'JurassicParkError';
6    this.timestamp = new Date();
7  }
8
9  logError() {
10    console.error(`[${this.timestamp.toISOString()}] ${this.name}: ${this.message}`);
11  }
12}
13
14// Błędy związane z dinozaurami
15class DinosaurError extends JurassicParkError {
16  constructor(message, species) {
17    super(message);
18    this.name = 'DinosaurError';
19    this.species = species;
20  }
21}
22
23// Konkretne typy błędów związanych z dinozaurami
24class DinosaurHealthError extends DinosaurError {
25  constructor(message, species, healthIssue, severity) {
26    super(message, species);
27    this.name = 'DinosaurHealthError';
28    this.healthIssue = healthIssue;
29    this.severity = severity;
30  }
31
32  requiresVeterinarian() {
33    return this.severity >= 7;
34  }
35}
36
37class DinosaurBehaviorError extends DinosaurError {
38  constructor(message, species, behavior, dangerLevel) {
39    super(message, species);
40    this.name = 'DinosaurBehaviorError';
41    this.behavior = behavior;
42    this.dangerLevel = dangerLevel;
43  }
44
45  requiresIntervention() {
46    return this.dangerLevel >= 5;
47  }
48}
49
50// Błędy związane z infrastrukturą parku
51class ParkInfrastructureError extends JurassicParkError {
52  constructor(message, systemName, location) {
53    super(message);
54    this.name = 'ParkInfrastructureError';
55    this.systemName = systemName;
56    this.location = location;
57  }
58}
59
60// Konkretne typy błędów infrastruktury
61class PowerSystemError extends ParkInfrastructureError {
62  constructor(message, location, affectedSystems) {
63    super(message, 'PowerSystem', location);
64    this.name = 'PowerSystemError';
65    this.affectedSystems = affectedSystems;
66  }
67
68  requiresBackupPower() {
69    return this.affectedSystems.includes('SecurityFences');
70  }
71}

Taka hierarchia pozwala na precyzyjną obsługę różnych typów błędów:

1async function monitorParkSystems() {
2  try {
3    await checkDinosaurHealth();
4    await checkEnclosureSecurity();
5    await checkPowerSystems();
6  } catch (error) {
7    // Logowanie wszystkich błędów
8    console.error(`[${new Date().toISOString()}] Error:`, error);
9
10    // Różne reakcje w zależności od typu błędu
11    if (error instanceof DinosaurHealthError) {
12      if (error.requiresVeterinarian()) {
13        await dispatchVeterinarianTeam(error.species, error.healthIssue, error.severity);
14      } else {
15        await scheduleRoutineCheckup(error.species);
16      }
17    }
18    else if (error instanceof DinosaurBehaviorError) {
19      if (error.requiresIntervention()) {
20        await dispatchBehavioralSpecialist(error.species, error.behavior);
21        if (error.dangerLevel >= 8) {
22          await prepareTransquilizers(error.species);
23        }
24      }
25    }
26    else if (error instanceof PowerSystemError) {
27      await notifyMaintenanceTeam(error.location, error.message);
28      if (error.requiresBackupPower()) {
29        await activateBackupGenerators(error.location);
30      }
31    }
32    else if (error instanceof JurassicParkError) {
33      // Ogólna obsługa dla wszystkich błędów parku
34      await logToSecuritySystem(error);
35    }
36    else {
37      // Nieznany typ błędu
38      console.error('Nieznany błąd:', error);
39    }
40  }
41}

Serializacja błędów

Jeden z problemów z obiektami błędów w JavaScript to ich serializacja. Domyślnie, gdy próbujemy przekonwertować błąd do JSON, tracimy większość informacji:

1const error = new Error('Test error');
2console.log(JSON.stringify(error)); // {} - puste obiekty!

Własne klasy błędów mogą rozwiązać ten problem implementując metodę

toJSON
:

1class SerializableError extends Error {
2  constructor(message) {
3    super(message);
4    this.name = this.constructor.name;
5  }
6
7  toJSON() {
8    return {
9      name: this.name,
10      message: this.message,
11      stack: this.stack,
12      // Dodatkowe właściwości specyficzne dla podklas
13      ...Object.getOwnPropertyNames(this)
14        .filter(prop => !['name', 'message', 'stack'].includes(prop))
15        .reduce((obj, prop) => {
16          obj[prop] = this[prop];
17          return obj;
18        }, {})
19    };
20  }
21}
22
23// Teraz możemy tworzyć serializowalne błędy
24class DinosaurEscapeError extends SerializableError {
25  constructor(message, species, location) {
26    super(message);
27    this.species = species;
28    this.location = location;
29    this.timestamp = new Date();
30  }
31}
32
33const error = new DinosaurEscapeError(
34  'Dinozaur uciekł z wybiegu',
35  'Velociraptor',
36  'Sektor B6'
37);
38
39// Teraz error można poprawnie serializować
40console.log(JSON.stringify(error, null, 2));
41/* Wynik:
42{
43  "name": "DinosaurEscapeError",
44  "message": "Dinozaur uciekł z wybiegu",
45  "stack": "DinosaurEscapeError: Dinozaur uciekł z wybiegu\n    at ...",
46  "species": "Velociraptor",
47  "location": "Sektor B6",
48  "timestamp": "2023-05-15T10:30:45.123Z"
49}
50*/

Jest to szczególnie przydatne, gdy musimy przesyłać informacje o błędach przez API lub zapisywać je w logach.

Obsługa błędów asynchronicznych z niestandardowymi klasami

Własne klasy błędów świetnie współpracują z asynchronicznym kodem:

1// Klasa błędu specyficzna dla API
2class APIError extends Error {
3  constructor(message, statusCode, endpoint) {
4    super(message);
5    this.name = 'APIError';
6    this.statusCode = statusCode;
7    this.endpoint = endpoint;
8    this.timestamp = new Date();
9  }
10
11  isServerError() {
12    return this.statusCode >= 500;
13  }
14
15  isClientError() {
16    return this.statusCode >= 400 && this.statusCode < 500;
17  }
18
19  isAuthError() {
20    return this.statusCode === 401 || this.statusCode === 403;
21  }
22}
23
24// Funkcja pobierająca dane z API, która używa niestandardowej klasy błędu
25async function fetchDinosaurData(dinoId) {
26  try {
27    const response = await fetch(`https://jurassic-park-api.com/dinosaurs/${dinoId}`);
28
29    if (!response.ok) {
30      throw new APIError(
31        `Błąd podczas pobierania danych o dinozaurze #${dinoId}`,
32        response.status,
33        `/dinosaurs/${dinoId}`
34      );
35    }
36
37    return await response.json();
38  } catch (error) {
39    if (error instanceof APIError) {
40      // Obsługa błędów API
41      if (error.isAuthError()) {
42        await refreshAuthToken();
43        return fetchDinosaurData(dinoId); // Ponowna próba po odświeżeniu tokenu
44      } else if (error.isServerError()) {
45        console.error('Błąd serwera, próba użycia zapasowego API...');
46        return fetchDinosaurDataFromBackupAPI(dinoId);
47      } else {
48        console.error(`Błąd klienta: ${error.message} (statusCode: ${error.statusCode})`);
49        throw error; // Przekazujemy błąd dalej
50      }
51    } else {
52      // Inny rodzaj błędu (np. sieciowy)
53      console.error('Nieoczekiwany błąd:', error);
54      throw new Error(`Nie udało się pobrać danych o dinozaurze #${dinoId}: ${error.message}`);
55    }
56  }
57}

Zaawansowany przykład: System raportowania incydentów

Zobaczmy, jak możemy wykorzystać własne klasy błędów w bardziej rozbudowanym przykładzie - systemie raportowania incydentów w Parku Jurajskim:

1// Bazowa klasa błędu dla incydentów
2class IncidentError extends Error {
3  constructor(message, location, severity) {
4    super(message);
5    this.name = 'IncidentError';
6    this.location = location;
7    this.severity = severity; // 1-10
8    this.timestamp = new Date();
9    this.resolved = false;
10    this.resolutionTime = null;
11  }
12
13  markAsResolved() {
14    this.resolved = true;
15    this.resolutionTime = new Date();
16    return this;
17  }
18
19  getIncidentDuration() {
20    if (!this.resolved) return null;
21    return (this.resolutionTime - this.timestamp) / 1000; // w sekundach
22  }
23
24  requiresEvacuation() {
25    return this.severity >= 7;
26  }
27
28  requiresNotification() {
29    return this.severity >= 4;
30  }
31
32  getSummary() {
33    return {
34      type: this.name,
35      message: this.message,
36      location: this.location,
37      severity: this.severity,
38      timestamp: this.timestamp,
39      status: this.resolved ? 'Rozwiązany' : 'Aktywny',
40      resolutionTime: this.resolutionTime
41    };
42  }
43
44  toJSON() {
45    return this.getSummary();
46  }
47}
48
49// Klasy dla różnych typów incydentów
50class DinosaurEscapeIncident extends IncidentError {
51  constructor(message, location, species, count, dangerLevel) {
52    super(message, location, dangerLevel);
53    this.name = 'DinosaurEscapeIncident';
54    this.species = species;
55    this.count = count;
56    this.lastSighting = location;
57  }
58
59  updateSighting(newLocation) {
60    this.lastSighting = newLocation;
61    return this;
62  }
63
64  getSummary() {
65    return {
66      ...super.getSummary(),
67      species: this.species,
68      count: this.count,
69      lastSighting: this.lastSighting
70    };
71  }
72}
73
74class SystemFailureIncident extends IncidentError {
75  constructor(message, location, system, affectedAreas, backupStatus) {
76    // Obliczenie poziomu krytyczności na podstawie systemu i dostępności backupu
77    const severity = SystemFailureIncident.calculateSeverity(system, backupStatus);
78
79    super(message, location, severity);
80    this.name = 'SystemFailureIncident';
81    this.system = system;
82    this.affectedAreas = affectedAreas;
83    this.backupStatus = backupStatus;
84  }
85
86  static calculateSeverity(system, backupStatus) {
87    const systemCriticality = {
88      'electric_fences': 10,
89      'security_doors': 9,
90      'surveillance': 7,
91      'climate_control': 6,
92      'guest_services': 3
93    };
94
95    // Jeśli backup działa, zmniejsz poziom krytyczności
96    const baseSeverity = systemCriticality[system] || 5;
97    return backupStatus === 'operational' ? Math.max(3, baseSeverity - 3) : baseSeverity;
98  }
99
100  requiresMaintenanceTeam() {
101    return true;
102  }
103
104  requiresEmergencyPower() {
105    return this.system === 'electric_fences' || this.system === 'security_doors';
106  }
107
108  getSummary() {
109    return {
110      ...super.getSummary(),
111      system: this.system,
112      affectedAreas: this.affectedAreas,
113      backupStatus: this.backupStatus,
114      requiresEmergencyPower: this.requiresEmergencyPower()
115    };
116  }
117}
118
119class VisitorIncident extends IncidentError {
120  constructor(message, location, visitorCount, injuryLevel, medicalStatus) {
121    // Obliczenie poziomu krytyczności na podstawie liczby gości i poziomu obrażeń
122    const severity = VisitorIncident.calculateSeverity(visitorCount, injuryLevel);
123
124    super(message, location, severity);
125    this.name = 'VisitorIncident';
126    this.visitorCount = visitorCount;
127    this.injuryLevel = injuryLevel; // 'none', 'minor', 'moderate', 'severe'
128    this.medicalStatus = medicalStatus; // 'not_required', 'dispatched', 'on_scene', 'evacuating'
129  }
130
131  static calculateSeverity(visitorCount, injuryLevel) {
132    const injurySeverity = {
133      'none': 1,
134      'minor': 3,
135      'moderate': 6,
136      'severe': 9
137    };
138
139    // Zwiększ poziom krytyczności dla większej liczby gości
140    const baseSeverity = injurySeverity[injuryLevel] || 5;
141    const visitorFactor = visitorCount > 10 ? 2 : visitorCount > 3 ? 1 : 0;
142
143    return Math.min(10, baseSeverity + visitorFactor);
144  }
145
146  requiresMedicalTeam() {
147    return this.injuryLevel !== 'none';
148  }
149
150  updateMedicalStatus(newStatus) {
151    this.medicalStatus = newStatus;
152
153    // Jeśli pomoc medyczna jest na miejscu i sytuacja nie jest krytyczna,
154    // możemy zmniejszyć poziom krytyczności
155    if (newStatus === 'on_scene' && this.injuryLevel !== 'severe') {
156      this.severity = Math.max(2, this.severity - 2);
157    }
158
159    return this;
160  }
161
162  getSummary() {
163    return {
164      ...super.getSummary(),
165      visitorCount: this.visitorCount,
166      injuryLevel: this.injuryLevel,
167      medicalStatus: this.medicalStatus,
168      requiresMedicalTeam: this.requiresMedicalTeam()
169    };
170  }
171}
172
173// System zarządzania incydentami
174class IncidentManagementSystem {
175  constructor() {
176    this.activeIncidents = new Map();
177    this.resolvedIncidents = [];
178    this.eventListeners = {
179      'new': [],
180      'update': [],
181      'resolve': []
182    };
183  }
184
185  reportIncident(incident) {
186    if (!(incident instanceof IncidentError)) {
187      throw new TypeError('Można raportować tylko obiekty typu IncidentError');
188    }
189
190    const incidentId = `INC-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
191    this.activeIncidents.set(incidentId, incident);
192
193    // Powiadomienie wszystkich słuchaczy o nowym incydencie
194    this._notifyListeners('new', { id: incidentId, incident });
195
196    // Automatyczne reakcje na incydent
197    this._triggerAutomatedResponses(incidentId, incident);
198
199    return incidentId;
200  }
201
202  updateIncident(incidentId, updateFn) {
203    if (!this.activeIncidents.has(incidentId)) {
204      throw new Error(`Incydent o ID ${incidentId} nie istnieje lub został już rozwiązany`);
205    }
206
207    const incident = this.activeIncidents.get(incidentId);
208    updateFn(incident);
209
210    // Powiadomienie wszystkich słuchaczy o aktualizacji incydentu
211    this._notifyListeners('update', { id: incidentId, incident });
212
213    return incident;
214  }
215
216  resolveIncident(incidentId, resolutionNotes = '') {
217    if (!this.activeIncidents.has(incidentId)) {
218      throw new Error(`Incydent o ID ${incidentId} nie istnieje lub został już rozwiązany`);
219    }
220
221    const incident = this.activeIncidents.get(incidentId);
222    incident.markAsResolved();
223    incident.resolutionNotes = resolutionNotes;
224
225    // Przenieś incydent z aktywnych do rozwiązanych
226    this.activeIncidents.delete(incidentId);
227    this.resolvedIncidents.push({ id: incidentId, incident });
228
229    // Powiadomienie wszystkich słuchaczy o rozwiązaniu incydentu
230    this._notifyListeners('resolve', { id: incidentId, incident });
231
232    return incident;
233  }
234
235  getActiveIncidents() {
236    return Array.from(this.activeIncidents.entries()).map(([id, incident]) => ({
237      id,
238      ...incident.getSummary()
239    }));
240  }
241
242  getIncidentById(incidentId) {
243    if (this.activeIncidents.has(incidentId)) {
244      return {
245        id: incidentId,
246        status: 'active',
247        ...this.activeIncidents.get(incidentId).getSummary()
248      };
249    }
250
251    const resolvedIncident = this.resolvedIncidents.find(inc => inc.id === incidentId);
252    if (resolvedIncident) {
253      return {
254        id: incidentId,
255        status: 'resolved',
256        ...resolvedIncident.incident.getSummary()
257      };
258    }
259
260    return null;
261  }
262
263  addEventListener(event, callback) {
264    if (!this.eventListeners[event]) {
265      throw new Error(`Nieznany typ zdarzenia: ${event}`);
266    }
267
268    this.eventListeners[event].push(callback);
269    return () => this.removeEventListener(event, callback); // Funkcja do usunięcia listenera
270  }
271
272  removeEventListener(event, callback) {
273    if (!this.eventListeners[event]) return false;
274
275    const index = this.eventListeners[event].indexOf(callback);
276    if (index !== -1) {
277      this.eventListeners[event].splice(index, 1);
278      return true;
279    }
280    return false;
281  }
282
283  _notifyListeners(event, data) {
284    if (!this.eventListeners[event]) return;
285
286    for (const listener of this.eventListeners[event]) {
287      try {
288        listener(data);
289      } catch (error) {
290        console.error(`Błąd w listenerze zdarzenia ${event}:`, error);
291      }
292    }
293  }
294
295  _triggerAutomatedResponses(incidentId, incident) {
296    console.log(`Automatyczna reakcja na incydent ${incidentId}:`, incident.name);
297
298    // Różne reakcje w zależności od typu incydentu
299    if (incident instanceof DinosaurEscapeIncident) {
300      if (incident.requiresEvacuation()) {
301        console.log(`EWAKUACJA! Uruchamianie protokołu ewakuacji dla: ${incident.location}`);
302        // evacuateSector(incident.location);
303      }
304
305      console.log(`Wysyłanie zespołu ACU (Asset Containment Unit) do lokalizacji: ${incident.lastSighting}`);
306      // dispatchACUTeam(incident.lastSighting, incident.species, incident.count);
307    }
308
309    else if (incident instanceof SystemFailureIncident) {
310      console.log(`Powiadomienie zespołu technicznego o awarii systemu: ${incident.system}`);
311      // notifyMaintenanceTeam(incident.system, incident.location);
312
313      if (incident.requiresEmergencyPower()) {
314          console.log('Uruchamianie systemu zasilania awaryjnego!');
315        // activateEmergencyPower(incident.affectedAreas);
316      }
317    }
318
319    else if (incident instanceof VisitorIncident) {
320      if (incident.requiresMedicalTeam()) {
321        console.log(`Wysyłanie zespołu medycznego do lokalizacji: ${incident.location}`);
322        // dispatchMedicalTeam(incident.location, incident.injuryLevel, incident.visitorCount);
323
324        // Aktualizacja statusu medycznego
325        this.updateIncident(incidentId, inc => inc.updateMedicalStatus('dispatched'));
326      }
327
328      if (incident.requiresEvacuation()) {
329        console.log(`Rozpoczynam ewakuację gości z: ${incident.location}`);
330        // evacuateVisitors(incident.location);
331      }
332    }
333
334    // Wspólne reakcje dla wszystkich incydentów
335    if (incident.requiresNotification()) {
336      console.log('Powiadamianie kierownictwa parku...');
337      // notifyParkManagement(incident.getSummary());
338    }
339  }
340}
341
342// Przykład użycia systemu incydentów
343function demoParkIncidentSystem() {
344  const incidentSystem = new IncidentManagementSystem();
345
346  // Dodajemy słuchacza dla nowych incydentów
347  incidentSystem.addEventListener('new', ({ id, incident }) => {
348    console.log(`NOWY INCYDENT [${id}]: ${incident.name} w lokalizacji ${incident.location}`);
349  });
350
351  // Raportowanie incydentu z dinozaurem
352  const raptorIncidentId = incidentSystem.reportIncident(
353    new DinosaurEscapeIncident(
354      'Ucieczka Velociraptorów z zagrody',
355      'Sektor C4',
356      'Velociraptor',
357      3,
358      9 // Wysoki poziom zagrożenia
359    )
360  );
361
362  // Aktualizacja incydentu - zmiana ostatniego spostrzeżenia
363  setTimeout(() => {
364    incidentSystem.updateIncident(raptorIncidentId, incident => {
365      incident.updateSighting('Sektor C5 - w pobliżu centrum odwiedzających');
366      console.log(`Aktualizacja: Raptory zauważono w ${incident.lastSighting}`);
367    });
368  }, 2000);
369
370  // Raportowanie incydentu systemowego
371  const powerIncidentId = incidentSystem.reportIncident(
372    new SystemFailureIncident(
373      'Awaria zasilania ogrodzeń elektrycznych',
374      'Sektor B1-B3',
375      'electric_fences',
376      ['TRex Paddock', 'Raptor Containment'],
377      'offline' // Backup nie działa
378    )
379  );
380
381  // Raportowanie incydentu z gośćmi
382  const visitorIncidentId = incidentSystem.reportIncident(
383    new VisitorIncident(
384      'Goście poza wyznaczoną trasą zwiedzania',
385      'Sektor D2',
386      4, // Liczba gości
387      'minor', // Drobne obrażenia
388      'not_required' // Status medyczny
389    )
390  );
391
392  // Wyświetlanie aktywnych incydentów
393  setTimeout(() => {
394    console.log('\n--- AKTYWNE INCYDENTY ---');
395    console.table(incidentSystem.getActiveIncidents());
396  }, 3000);
397
398  // Rozwiązanie incydentu z gośćmi
399  setTimeout(() => {
400    incidentSystem.resolveIncident(
401      visitorIncidentId,
402      'Goście zostali bezpiecznie eskortowani z powrotem na wyznaczoną trasę zwiedzania.'
403    );
404    console.log(`Incydent ${visitorIncidentId} został oznaczony jako rozwiązany.`);
405  }, 4000);
406
407  // Rozwiązanie incydentu z zasilaniem
408  setTimeout(() => {
409    incidentSystem.resolveIncident(
410      powerIncidentId,
411      'Zasilanie przywrócone, ogrodzenia ponownie aktywne.'
412    );
413    console.log(`Incydent ${powerIncidentId} został oznaczony jako rozwiązany.`);
414  }, 5000);
415
416  // Rozwiązanie incydentu z raptorami
417  setTimeout(() => {
418    incidentSystem.resolveIncident(
419      raptorIncidentId,
420      'Wszystkie 3 Velociraptory złapane i bezpiecznie przetransportowane z powrotem do zagrody.'
421    );
422    console.log(`Incydent ${raptorIncidentId} został oznaczony jako rozwiązany.`);
423
424    // Wyświetlenie podsumowania incydentu
425    const incidentDetails = incidentSystem.getIncidentById(raptorIncidentId);
426    console.log('\n--- SZCZEGÓŁY ROZWIĄZANEGO INCYDENTU ---');
427    console.log(JSON.stringify(incidentDetails, null, 2));
428  }, 6000);
429}
430
431// Uruchomienie demonstracji
432// demoParkIncidentSystem();

Dobre praktyki tworzenia własnych klas błędów

  1. Nazywaj błędy znacząco - nazwa powinna jasno wskazywać, co poszło nie tak
  2. Twórz hierarchię błędów - używaj dziedziczenia, aby stworzyć logiczną hierarchię klas błędów
  3. Dodawaj kontekst - zawsze dołączaj dodatkowe informacje, które pomogą w diagnozie i obsłudze błędu
  4. Implementuj metody pomocnicze - dodawaj metody, które ułatwią kategorizację i obsługę błędów
  5. Dbaj o serializację - dodaj metodę
    toJSON()
    , aby błędy można było łatwo logować i przesyłać
  6. Zachowaj ślad stosu - upewnij się, że wywołujesz
    super(message)
    w konstruktorze, aby zachować ślad stosu
  7. Dokumentuj swoje klasy błędów - inni programiści (i przyszły ty) powinni wiedzieć, kiedy i jak używać twoich klas błędów

Wnioski

Własne klasy błędów to potężne narzędzie w arsenale programisty JavaScript. W złożonych aplikacjach, takich jak system zarządzania Parkiem Jurajskim, precyzyjna kategoryzacja i obsługa różnych typów błędów jest kluczowa dla utrzymania bezpieczeństwa i stabilności.

Pamiętaj, że jak powiedział Dr. Ian Malcolm: "Życie... uh... znajdzie sposób." To samo dotyczy błędów w kodzie. Najlepszym podejściem jest nie udawać, że błędy nie istnieją, ale spodziewać się ich i być przygotowanym na ich obsługę w jak najbardziej elegancki i skuteczny sposób.

W następnym ćwiczeniu wykorzystamy wszystkie poznane techniki do implementacji pełnej aplikacji, która będzie demonstrować moduły, asynchroniczność i obsługę błędów w praktyce.

Ir a CodeWorlds