Cómo cancelar una suscripción en Angular2

81

¿Cómo se cancela una suscripción en Angular2? RxJS parece tener un método de eliminación, pero no puedo averiguar cómo acceder a él. Así que tengo un código que tiene acceso a un EventEmitter y se suscribe a él, como este:

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

¿Cómo puedo usar mySubscriptionpara cancelar la suscripción?

Michael Oryl
fuente
2
FYI: si va a hacer cosas reactivas, use un sujeto, en lugar del EventEmitter de Angular; no hay garantía de que siga siendo una superclase de sujeto. Utilice EventEmitter solo para eventos de @Output.
robwormald

Respuestas:

121

¿Quiere darse de baja?

mySubscription.unsubscribe();
Kendaleiv
fuente
4
Buen señor. Juro que ya lo intenté. Miré la fuente de RxJS y eso parecía ser lo que era. Debo haber tenido un error diferente que me causó problemas. Gracias.
Michael Oryl
1
esto funciona bien, pero tengo curiosidad por saber cuál es el tipo de mySubscriptionTypeScript. Quiero evitar escribir mySubscription: anyen mi clase.
paradite
11
@paradite import { Subscription } from "rxjs";y para darse de baja if (!mySubscription.closed) { mySubscription.unsubscribe(); }.
Llyle
9
import { Subscription } from 'rxjs/Subscription';ayudará a mantener el tamaño de su paquete bajo
paul
50

Pensé que también había puesto mis dos centavos. Yo uso este patrón:

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();
        });
    }
}

EDITAR

Leí la documentación el otro día y encontré un patrón más recomendado:

ReactiveX / RxJS / Suscripción

Pros:

Administra las nuevas suscripciones internamente y agrega algunas comprobaciones ordenadas. Preferiría este método en la función :).

Contras:

No está 100% claro cuál es el flujo de código y cómo se ven afectadas las suscripciones. Tampoco está claro (solo mirando el código) cómo se manejan las suscripciones cerradas y si todas las suscripciones se cierran si se llama a cancelar suscripción.

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
fuente
2
Yo también uso esto, se adapta bien cuantas más suscripciones tenga en un componente.
weltschmerz
1
Sin "magia", es por diseño: "Las suscripciones también se pueden juntar, de modo que una llamada para cancelar la suscripción () de una Suscripción puede cancelar la suscripción de varias Suscripciones. Puede hacerlo" agregando "una suscripción a otra:" github. com / ReactiveX / rxjs / blob / master / doc / subscription.md
Meetai.com
Hay otro enfoque claro en el que podría estar interesado. Utilizando takeWhileoperador. Enfoque descrito aquí: brianflove.com/2016/12/11/anguar-2-unsubscribe-observables
vdshb
Consulte una solución alternativa , que está utilizando takeUntil. Hay una discusión rica e interesante, considerando diferentes opciones.
Alex Klaus
8

EDITAR: Esto no se aplica a RxJS 5, que es lo que usa angular2.

Pensé que estaba buscando el método de eliminación en Desechables .

el método de suscripción devuelve un desechable ( enlace )

Parece que no puedo encontrarlo más explícitamente en los documentos, pero esto funciona ( jsbin ):

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

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

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

Extrañamente, la cancelación de la suscripción parece funcionar para ti, mientras que a mí no me funciona ...

Niklas Fasching
fuente
Probablemente uses diferentes versiones. Angular2 está en Rxjs5, ¿no?
user3743222
Sí, Angular 2 usa RxJS 5.
Michael Oryl
1
Eso es. Se puede encontrar aquí . El cambio se realizó para cumplir con la especificación observable ES7
Niklas Fasching
4

Demasiadas explicaciones diferentes sobre la cancelación de suscripción en Observables para ng2, me llevó años encontrar la respuesta correcta. A continuación se muestra un ejemplo de trabajo (estaba tratando de acelerar el movimiento del mouse).

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
fuente
2
ngOnDestroy(){
   mySubscription.unsubscribe();
}

Prefiere darse de baja de rxjs unsubscribe's mientras destruye el componente, es decir, elimina del DOM para evitar pérdidas de memoria innecesarias

Prithvi Uppalapati
fuente
2

Personalmente, prefiero usar un sujeto para cerrar todas las suscripciones que un componente pueda tener en el paso del ciclo de vida de destrucción, lo que se puede lograr de esta manera:

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
fuente
2

El enfoque recomendado es utilizar operadores RxJS como el operador takeUntil . A continuación se muestra el fragmento de código que muestra cómo usarlo: -

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();
    }
}

Puede encontrar una explicación detallada del tema aquí.

Yatharth Varshney
fuente
por qué necesitamos llamar a ngUnsubscribe.next (); en el método ngOnDestroy ()?
Biswa Bandhu Bhandary
1

Utilizar

if(mySubscription){
  mySubscription.unsubscribe();
}
Anshul
fuente
1
En realidad debería serif (!mySubscription.closed) { mySubscription.unsubscribe(); }
Llyle
-1
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { SomeAPIService } from '../some_file/someAPIService.service.ts

@Component({
  templateUrl: './your_Page.html',
  styleUrls: ['./your_Styles.scss']
})

export class (your class) implements OnInit, OnDestroy {
   // This is a subject variable at it simplest form 
     private unsubscribe$ = new Subject<void>();

     constructor (private someAPIService : SomeAPIService) {}
   
     ngOnit(): void { 
       this.someAPIService.getTODOlist({id:1})
        .pipe(takeUntil(this.unsubscribe$))
         .subscribe((value: SomeVariable) => {
         // What ever value you need is SomeVariable
      },)
    }


     ngOnDestroy(): void {
    // clears all, page subscriptions 
      this.unsubscribe$.next();
      this.unsubscribe$.complete();
     }
`}
J Crain
fuente