So kündigen Sie ein Abonnement in Angular2

80

Wie kündigt man ein Abonnement in Angular2? RxJS scheint eine Entsorgungsmethode zu haben, aber ich kann nicht herausfinden, wie ich darauf zugreifen soll. Ich habe also Code, der Zugriff auf einen EventEmitter hat und ihn wie folgt abonniert:

var mySubscription = someEventEmitter.subscribe(
    (val) => {
        console.log('Received:', val);
    },
    (err) => {
        console.log('Received error:', err);
    },
    () => {
        console.log('Completed');
    }
);

Wie kann ich mySubscriptiondas Abonnement kündigen?

Michael Oryl
quelle
2
Zu Ihrer Information: Wenn Sie reaktive Dinge tun möchten, verwenden Sie einen Betreff anstelle von Angulars EventEmitter. Es gibt keine Garantie dafür, dass es sich um eine Oberklasse von Betreff handelt. Verwenden Sie EventEmitter nur für @ Output-Ereignisse.
Robwormald

Antworten:

118

Möchten Sie sich abmelden?

mySubscription.unsubscribe();
kendaleiv
quelle
4
Guter Herr. Ich habe geschworen, dass ich das schon versucht habe. Ich schaute auf die RxJS-Quelle und das schien so zu sein. Ich muss einen anderen Fehler gehabt haben, der mir Probleme verursacht hat. Vielen Dank.
Michael Oryl
1
Das funktioniert gut, aber ich bin gespannt, welche Art von mySubscriptionTypeScript verwendet wird. Ich möchte vermeiden, mySubscription: anyin meiner Klasse zu schreiben .
Paradite
11
@paradite import { Subscription } from "rxjs";und zur Abmeldung if (!mySubscription.closed) { mySubscription.unsubscribe(); }.
Llyle
9
import { Subscription } from 'rxjs/Subscription';wird helfen, Ihre Paketgröße niedrig zu halten
Paul
50

Ich dachte, ich stecke auch meine zwei Cent ein. Ich benutze dieses Muster:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

@Component({
    selector: 'my-component',
    templateUrl: 'my.component.html'
})
export class MyComponent implements OnInit, OnDestroy {

    private subscriptions: Array<Subscription> = [];

    public ngOnInit(): void {
        this.subscriptions.push(this.someService.change.subscribe(() => {
            [...]
        }));

        this.subscriptions.push(this.someOtherService.select.subscribe(() => {
            [...]
        }));
    }

    public ngOnDestroy(): void {
        this.subscriptions.forEach((subscription: Subscription) => {
            subscription.unsubscribe();
        });
    }
}

BEARBEITEN

Ich habe neulich die Dokumentation gelesen und ein empfohleneres Muster gefunden:

ReactiveX / RxJS / Abonnement

Vorteile:

Es verwaltet die neuen Abonnements intern und fügt einige ordentliche Überprüfungen hinzu. Würde diese Methode in der Funktion bevorzugen :).

Nachteile:

Es ist nicht 100% klar, wie der Codefluss aussieht und wie Abonnements betroffen sind. Es ist auch nicht klar (nur anhand des Codes), wie es mit geschlossenen Abonnements umgeht und ob alle Abonnements geschlossen werden, wenn das Abbestellen aufgerufen wird.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';

@Component({
    selector: 'my-component',
    templateUrl: 'my.component.html'
})
export class MyComponent implements OnInit, OnDestroy {

    private subscription: Subscription = new Subscription();

    public ngOnInit(): void {
        this.subscription.add(this.someService.change.subscribe(() => {
            [...]
        }));

        this.subscription.add(this.someOtherService.select.subscribe(() => {
            [...]
        }));
    }

    public ngOnDestroy(): void {
        /*
         * magic kicks in here: All subscriptions which were added
         * with "subscription.add" are canceled too!
         */
        this.subscription.unsubscribe();
    }
}
Nightking
quelle
2
Ich benutze dies auch, es skaliert gut, je mehr Abonnements Sie in einer Komponente haben.
Weltschmerz
1
Keine "Magie" - es ist beabsichtigt: "Abonnements können auch zusammengestellt werden, sodass ein Aufruf eines Abonnements () eines Abonnements mehrere Abonnements abbestellen kann. Sie können dies tun, indem Sie ein Abonnement zu einem anderen" hinzufügen ":" github. com / ReactiveX / rxjs / blob / master / doc / abonnement.md
Meetai.com
Es gibt noch einen anderen klaren Ansatz, an dem Sie interessiert sein könnten. Verwenden des takeWhileOperators. Ansatz hier beschrieben: brianflove.com/2016/12/11/anguar-2-unsubscribe-observables
vdshb
Schauen Sie sich eine alternative Lösung an , die takeUntil verwendet. Es gibt eine reiche und interessante Diskussion, die verschiedene Optionen in Betracht zieht.
Alex Klaus
8

BEARBEITEN: Dies gilt nicht für RxJS 5, das von angle2 verwendet wird.

Ich hätte gedacht, Sie suchen nach der Entsorgungsmethode für Einwegartikel .

Die Subscribe-Methode gibt ein Disposable ( Link ) zurück.

Ich kann es nicht expliziter in den Dokumenten finden, aber das funktioniert ( jsbin ):

var observable = Rx.Observable.interval(100);

var subscription = observable.subscribe(function(value) {
   console.log(value);
});

setTimeout(function() {
  subscription.dispose();           
}, 1000)

Seltsamerweise scheint das Abbestellen für Sie zu funktionieren, während es für mich nicht funktioniert ...

Niklas Fasching
quelle
Sie verwenden wahrscheinlich verschiedene Versionen. Angular2 ist auf Rxjs5, nicht wahr?
user3743222
Ja, Angular 2 verwendet RxJS 5.
Michael Oryl
1
Das ist es. Kann hier gefunden werden . Die Änderung wurde vorgenommen, um der ES7 Observable Spec
Niklas Fasching
4

Viel zu viele verschiedene Erklärungen zum Abbestellen von Observables für ng2 haben mich ewig gekostet, um die richtige Antwort zu finden. Unten ist ein funktionierendes Beispiel (ich habe versucht, die Mausbewegung zu drosseln).

import {Injectable, OnDestroy} from "@angular/core";
import {Subscription} from "rxjs";

@Injectable()
export class MyClass implements OnDestroy {
  
  mouseSubscription: Subscription; //Set a variable for your subscription
  
  myFunct() {
    // I'm trying to throttle mousemove
    const eachSecond$ = Observable.timer(0, 1000);
    const mouseMove$ = Observable.fromEvent<MouseEvent>(document, 'mousemove');
    const mouseMoveEachSecond$ = mouseMove$.sample(eachSecond$);
    
    this.mouseSubscription = mouseMoveEachSecond$.subscribe(() => this.doSomethingElse());
  }

  doSomethingElse() {
    console.log("mouse moved");
  }
  
  stopNow() {
    this.mouseSubscription.unsubscribe();
  }
  
  ngOnDestroy() {
    this.mouseSubscription.unsubscribe();
  }
  
}

Joe Keene
quelle
2
ngOnDestroy(){
   mySubscription.unsubscribe();
}

Ziehen Sie es vor, rxjs abzumelden, während Sie die Komponente zerstören, dh aus dem DOM entfernen, um unnötige Speicherlecks zu vermeiden

Prithvi Uppalapati
quelle
2

Ich persönlich bevorzuge es, einen Betreff zu verwenden, um alle Abonnements zu schließen, die eine Komponente im Schritt "Lebenszyklus zerstören" möglicherweise hat. Dies kann auf folgende Weise erreicht werden:

import { Component} from '@angular/core';
import { Subject } from "rxjs/Rx";

@Component({
  selector:    'some-class-app',
  templateUrl: './someClass.component.html',
  providers:   []
})

export class SomeClass {  

  private ngUnsubscribe: Subject<void> = new Subject<void>(); //This subject will tell every subscriptions to stop when the component is destroyed.

  //**********
  constructor() {}

  ngOnInit() {

    this.http.post( "SomeUrl.com", {}, null ).map( response => {

      console.log( "Yay." );

    }).takeUntil( this.ngUnsubscribe ).subscribe(); //This is where you tell the subscription to stop whenever the component will be destroyed.
  }

  ngOnDestroy() {

    //This is where we close any active subscription.
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }
}
Jahrenski
quelle
1

Verwenden

if(mySubscription){
  mySubscription.unsubscribe();
}
Anshul
quelle
1
Eigentlich sollteif (!mySubscription.closed) { mySubscription.unsubscribe(); }
Llyle
1

Der empfohlene Ansatz besteht darin, RxJS-Operatoren wie den takeUntil- Operator zu verwenden. Unten sehen Sie das Code-Snippet, das zeigt, wie es verwendet wird: -

import { Component, OnInit, OnDestroy } from '@angular/core';
import { interval, Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Component({
    selector: 'app-root',
    templateUrl: './app.component.html'
})
export class AppComponent implements OnInit, OnDestroy {
    private ngUnsubscribe = new Subject();

    constructor() { }

    ngOnInit() {
        var observable1 = interval(1000);
        var observable2 = interval(2000);

        observable1.pipe(takeUntil(this.ngUnsubscribe)).subscribe(x => console.log('observable1: ' + x));
        observable2.pipe(takeUntil(this.ngUnsubscribe)).subscribe(x => console.log('observable2: ' + x));
    }

    ngOnDestroy() {
        this.ngUnsubscribe.next();
        this.ngUnsubscribe.complete();
    }
}

Eine ausführliche Erläuterung des Themas finden Sie hier

Yatharth Varshney
quelle
warum wir ngUnsubscribe.next () aufrufen müssen; in der ngOnDestroy () -Methode?
Biswa Bandhu Bhandary