finalize und complete - Ressourcenfreigabe und Stream-Abschlussverarbeitung
In RxJS ist es wichtig, die Stream-Beendigung und Ressourcenfreigabe angemessen zu verwalten. Diese Seite erklärt den Mechanismus des finalize-Operators und der complete-Benachrichtigung.
finalize - Operator zur Ressourcenfreigabe
Der finalize-Operator ist ein Operator, der den angegebenen Cleanup-Code ausführt, wenn ein Observable entweder durch Abschluss, Fehler oder Abmeldung beendet wird. finalize wird immer einmal bei Stream-Beendigung aufgerufen und wird nie mehrfach aufgerufen.
🌐 RxJS Official Documentation - finalize
Grundlegende Verwendung von finalize
import { of } from 'rxjs';
import { finalize, tap } from 'rxjs';
// Variable zur Verwaltung des Ladestatus
let isLoading = true;
// Erfolgreicher Stream
of('Daten')
.pipe(
tap((data) => console.log('Datenverarbeitung:', data)),
// Wird in allen Fällen ausgeführt: Erfolg, Fehler oder Abbruch
finalize(() => {
isLoading = false;
console.log('Ladestatus zurückgesetzt:', isLoading);
})
)
.subscribe({
next: (value) => console.log('Wert:', value),
complete: () => console.log('Abgeschlossen'),
});
// Ausgabe:
// Datenverarbeitung: Daten
// Wert: Daten
// Abgeschlossen
// Ladestatus zurückgesetzt: falsefinalize bei Fehlerauftreten
import { throwError } from 'rxjs';
import { finalize, catchError } from 'rxjs';
let isLoading = true;
throwError(() => new Error('Datenabruffehler'))
.pipe(
catchError((err) => {
console.error('Fehlerverarbeitung:', err.message);
throw err; // Fehler erneut werfen
}),
finalize(() => {
isLoading = false;
console.log('Ressourcenfreigabe nach Fehler:', isLoading);
})
)
.subscribe({
next: (value) => console.log('Wert:', value),
error: (err) => console.error('Fehler beim Subscriber:', err.message),
complete: () => console.log('Abgeschlossen'), // Wird bei Fehler nicht aufgerufen
});
// Ausgabe:
// Fehlerverarbeitung: Datenabruffehler
// Fehler beim Subscriber: Datenabruffehler
// Ressourcenfreigabe nach Fehler: falsefinalize bei Abmeldung
import { interval } from 'rxjs';
import { finalize } from 'rxjs';
let resource = 'Aktiv';
// Zählt jede Sekunde
const subscription = interval(1000)
.pipe(
finalize(() => {
resource = 'Freigegeben';
console.log('Ressourcenstatus:', resource);
})
)
.subscribe((count) => {
console.log('Zählung:', count);
// Nach 3 Zählungen manuell abmelden
if (count >= 2) {
subscription.unsubscribe();
}
});
// Ausgabe:
// Zählung: 0
// Zählung: 1
// Zählung: 2
// Ressourcenstatus: Freigegebenfinalize ist nicht nur bei Fehlerauftreten wirksam, sondern auch bei normalem Abschluss und manueller Abmeldung (unsubscribe), wenn Sie zuverlässig Cleanup-Verarbeitung durchführen möchten.
complete - Benachrichtigung über normale Stream-Beendigung
Wenn ein Observable normal beendet wird, wird der complete-Callback des Observers aufgerufen. Dies ist der letzte Schritt im Lebenszyklus eines Observable.
Automatisches complete
Einige Observables werden automatisch abgeschlossen, wenn bestimmte Bedingungen erfüllt sind.
import { of } from 'rxjs';
import { take } from 'rxjs';
// Endliche Sequenzen werden automatisch abgeschlossen
of(1, 2, 3).subscribe({
next: (value) => console.log('Wert:', value),
complete: () => console.log('Endlicher Stream abgeschlossen'),
});
// interval + take begrenzt Stream
interval(1000)
.pipe(
take(3) // Nach 3 Werten abgeschlossen
)
.subscribe({
next: (value) => console.log('Zählung:', value),
complete: () => console.log('Begrenzter Stream abgeschlossen'),
});
// Ausgabe:
// Wert: 1
// Wert: 2
// Wert: 3
// Endlicher Stream abgeschlossen
// Zählung: 0
// Zählung: 1
// Zählung: 2
// Begrenzter Stream abgeschlossenManuelles complete
Bei Subject oder benutzerdefinierten kann complete manuell aufgerufen werden.
import { Subject } from 'rxjs';
const subject = new Subject<number>();
subject.subscribe({
next: (value) => console.log('Wert:', value),
complete: () => console.log('Subject abgeschlossen'),
});
subject.next(1);
subject.next(2);
subject.complete(); // Manuell abschließen
subject.next(3); // Nach Abschluss ignoriert
// Ausgabe:
// Wert: 1
// Wert: 2
// Subject abgeschlossenUnterschied zwischen finalize und complete
Verstehen Sie die wichtigen Unterschiede.
Ausführungszeitpunkt
complete: Wird nur bei normalem Abschluss des Observable aufgerufenfinalize: Wird aufgerufen, wenn Observable durch Abschluss, Fehler oder Abmeldung beendet wird
Verwendungszweck
complete: Benachrichtigung über normalen Abschluss erhalten (Verarbeitung bei Erfolg)finalize: Ressourcenfreigabe und Cleanup zuverlässig durchführen (immer auszuführende Verarbeitung unabhängig von Erfolg oder Fehler)
Praktische Anwendungsfälle
API-Aufruf und Ladestatus-Verwaltung
import { ajax } from 'rxjs/ajax';
import { finalize, catchError } from 'rxjs';
import { of } from 'rxjs';
// Ladestatus
let isLoading = false;
function fetchData(id: string) {
// Laden beginnen
isLoading = true;
const loading = document.createElement('p');
loading.style.display = 'block';
document.body.appendChild(loading);
// document.getElementById('loading')!.style.display = 'block';
// API-Anfrage
return ajax.getJSON(`https://jsonplaceholder.typicode.com/posts/${id}`).pipe(
catchError((error) => {
console.error('API-Fehler:', error);
return of({ error: true, message: 'Datenabruf fehlgeschlagen' });
}),
// Laden unabhängig von Erfolg oder Fehler beenden
finalize(() => {
isLoading = false;
loading!.style.display = 'none';
console.log('Ladestatus-Reset abgeschlossen');
})
);
}
// Verwendungsbeispiel
fetchData('123').subscribe({
next: (data) => console.log('Daten:', data),
complete: () => console.log('Datenabruf abgeschlossen'),
});
// Ausgabe:
// API-Fehler: AjaxErrorImpl {message: 'ajax error', name: 'AjaxError', xhr: XMLHttpRequest, request: {…}, status: 0, …}
// Daten: {error: true, message: 'Datenabruf fehlgeschlagen'}
// Datenabruf abgeschlossen
// Ladestatus-Reset abgeschlossen
// GET https://jsonplaceholder.typicode.com/posts/123 net::ERR_NAME_NOT_RESOLVEDRessourcen-Cleanup
import { interval } from 'rxjs';
import { finalize, takeUntil } from 'rxjs';
import { Subject } from 'rxjs';
class ResourceManager {
private destroy$ = new Subject<void>();
private timerId: number | null = null;
constructor() {
// Ressourceninitialisierung
this.timerId = window.setTimeout(() => console.log('Timer ausgeführt'), 10000);
// Periodische Verarbeitung
interval(1000)
.pipe(
// Stoppen bei Komponentenvernichtung
takeUntil(this.destroy$),
// Ressourcenfreigabe sicherstellen
finalize(() => {
console.log('Intervall gestoppt');
})
)
.subscribe((count) => {
console.log('Wird ausgeführt...', count);
});
}
dispose() {
// Vernichtungsverarbeitung
if (this.timerId) {
window.clearTimeout(this.timerId);
this.timerId = null;
}
// Stream-Stoppsignal
this.destroy$.next();
this.destroy$.complete();
console.log('Ressourcenmanager-Vernichtung abgeschlossen');
}
}
// Verwendungsbeispiel
const manager = new ResourceManager();
// Nach 5 Sekunden vernichten
setTimeout(() => {
manager.dispose();
}, 5000);
// Ausgabe:
// Wird ausgeführt... 0
// Wird ausgeführt... 1
// Wird ausgeführt... 2
// Wird ausgeführt... 3
// Wird ausgeführt... 4
// Intervall gestoppt
// Ressourcenmanager-Vernichtung abgeschlossenBest Practices
- Ressourcen immer freigeben:
finalizeverwenden, um Cleanup bei Stream-Beendigung zu garantieren - Ladestatus-Verwaltung:
finalizeverwenden, um Ladestatus immer zurückzusetzen - Komponenten-Lebenszyklus-Verwaltung:
takeUntilundfinalizekombinieren, um Ressourcen bei Komponentenvernichtung zu bereinigen (besonders empfohlen in Angular usw.) - Verwendung mit Fehlerbehandlung:
catchErrorundfinalizekombinieren, um Fallback-Verarbeitung nach Fehler und zuverlässiges Cleanup zu realisieren - Abschlussstatus verstehen:
complete-Callback verwenden, um zu beurteilen, ob Stream normal abgeschlossen wurde
Zusammenfassung
finalize und complete sind wichtige Werkzeuge für Ressourcenverwaltung und Verarbeitungsabschluss in RxJS. finalize ist optimal für Ressourcenfreigabe, da es unabhängig davon, wie der Stream endet, zuverlässig ausgeführt wird. Andererseits wird complete verwendet, wenn Sie Verarbeitung bei normalem Abschluss durchführen möchten. Durch angemessene Kombination dieser können Sie Speicherlecks verhindern und zuverlässige Anwendungen erstellen.