Angolare / RxJs Quando dovrei annullare l'iscrizione a "Abbonamento"


720

Quando devo archiviare le Subscriptionistanze e invocare unsubscribe()durante il ciclo di vita di NgOnDestroy e quando posso semplicemente ignorarle?

Il salvataggio di tutti gli abbonamenti introduce molta confusione nel codice componente.

La Guida client HTTP ignora le sottoscrizioni in questo modo:

getHeroes() {
  this.heroService.getHeroes()
                   .subscribe(
                     heroes => this.heroes = heroes,
                     error =>  this.errorMessage = <any>error);
}

Allo stesso tempo, la Guida a destinazione e navigazione afferma che:

Alla fine, navigheremo altrove. Il router rimuoverà questo componente dal DOM e lo distruggerà. Dobbiamo ripulire dopo noi stessi prima che ciò accada. In particolare, dobbiamo annullare l'iscrizione prima che Angular distrugga il componente. In caso contrario, si potrebbe creare una perdita di memoria.

Annulliamo l'iscrizione al nostro Observablenel ngOnDestroymetodo.

private sub: any;

ngOnInit() {
  this.sub = this.route.params.subscribe(params => {
     let id = +params['id']; // (+) converts string 'id' to a number
     this.service.getHero(id).then(hero => this.hero = hero);
   });
}

ngOnDestroy() {
  this.sub.unsubscribe();
}

21
Immagino Subscriptionche si http-requestspossa ignorare, poiché chiamano solo onNextuna volta e poi chiamano onComplete. Il Routerinvece chiama onNextripetutamente e potrebbe mai chiamare onComplete(non sicuro di questo ...). Lo stesso vale per Observables da Events. Quindi immagino che dovrebbero essere unsubscribed.
Springrbua,

1
@ gt6707a Lo stream viene completato (o non completato) indipendentemente da qualsiasi osservazione di tale completamento. I callback (l'osservatore) forniti alla funzione di sottoscrizione non determinano se le risorse sono allocate. È la chiamata a subscribese stesso che potenzialmente alloca le risorse a monte.
seangwright,

Risposte:


981

--- Modifica 4 - Risorse aggiuntive (2018/09/01)

In un recente episodio di Adventures in Angular, Ben Lesh e Ward Bell discutono dei problemi su come / quando annullare l'iscrizione a un componente. La discussione inizia alle 1:05:30 circa.

Menzioni di rione right now there's an awful takeUntil dance that takes a lot of machinerye menzioni di Shai Reznik Angular handles some of the subscriptions like http and routing.

In risposta Ben menziona che in questo momento ci sono discussioni per consentire a Observables di agganciarsi agli eventi del ciclo di vita dei componenti angolari e Ward suggerisce un osservabile di eventi del ciclo di vita a cui un componente potrebbe iscriversi come un modo per sapere quando completare gli osservabili mantenuti come stato interno del componente.

Detto questo, ora abbiamo principalmente bisogno di soluzioni, quindi ecco alcune altre risorse.

  1. Una raccomandazione per lo takeUntil()schema da parte del membro del team principale di RxJ Nicholas Jamieson e una regola tslint per aiutare a farla rispettare. https://ncjamieson.com/avoiding-takeuntil-leaks/

  2. Pacchetto npm leggero che espone un operatore Observable che accetta un'istanza del componente ( this) come parametro e annulla automaticamente l'iscrizione durante ngOnDestroy. https://github.com/NetanelBasal/ngx-take-until-destroy

  3. Un'altra variante di quanto sopra con un'ergonomia leggermente migliore se non stai eseguendo build AOT (ma dovremmo tutti fare AOT ora). https://github.com/smnbbrv/ngx-rx-collector

  4. Direttiva personalizzata *ngSubscribeche funziona come una pipe asincrona ma crea una vista incorporata nel modello in modo da poter fare riferimento al valore "non imballato" in tutto il modello. https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

Cito in un commento al blog di Nicholas che un uso eccessivo di takeUntil()potrebbe essere un segno che il componente sta provando a fare troppo e che occorre considerare la separazione dei componenti esistenti in componenti Feature e Presentational . È quindi possibile | asyncosservare l'Osservabile dal componente Feature in uno Inputdel componente Presentational, il che significa che non sono necessari abbonamenti ovunque. Leggi di più su questo approccio qui

--- Modifica 3 - La soluzione "ufficiale" (2017/04/09)

Ho parlato con Ward Bell di questa domanda a NGConf (gli ho anche mostrato questa risposta che ha detto che era corretta) ma mi ha detto che il team di docenti di Angular aveva una soluzione a questa domanda che non è stata pubblicata (anche se stanno lavorando per ottenere l'approvazione ). Mi disse anche che avrei potuto aggiornare la mia risposta SO con l'imminente raccomandazione ufficiale.

La soluzione che dovremmo usare in futuro è quella di aggiungere un private ngUnsubscribe = new Subject();campo a tutti i componenti che hanno .subscribe()chiamate a Observableall'interno del loro codice di classe.

Chiamiamo quindi i this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();nostri ngOnDestroy()metodi.

La salsa segreta (come già notato da @metamaker ) è quella di chiamare takeUntil(this.ngUnsubscribe)prima di ciascuna delle nostre .subscribe()chiamate che garantirà che tutti gli abbonamenti vengano ripuliti quando il componente viene distrutto.

Esempio:

import { Component, OnDestroy, OnInit } from '@angular/core';
// RxJs 6.x+ import paths
import { filter, startWith, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { BookService } from '../books.service';

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

    constructor(private booksService: BookService) { }

    ngOnInit() {
        this.booksService.getBooks()
            .pipe(
               startWith([]),
               filter(books => books.length > 0),
               takeUntil(this.ngUnsubscribe)
            )
            .subscribe(books => console.log(books));

        this.booksService.getArchivedBooks()
            .pipe(takeUntil(this.ngUnsubscribe))
            .subscribe(archivedBooks => console.log(archivedBooks));
    }

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

Nota: è importante aggiungere l' takeUntiloperatore come ultimo per evitare perdite con osservabili intermedi nella catena dell'operatore.

--- Modifica 2 (28/12/2016)

Fonte 5

Il tutorial di Angular, il capitolo Routing ora afferma quanto segue: "Il router gestisce gli osservabili che fornisce e localizza gli abbonamenti. Gli abbonamenti vengono ripuliti quando il componente viene distrutto, proteggendo da perdite di memoria, quindi non è necessario annullare l'iscrizione a il percorso è osservabile. " - Mark Rajcok

Ecco una discussione sulle questioni di Github per i documenti angolari riguardanti gli osservabili del router in cui Ward Bell menziona che è in corso un chiarimento per tutto ciò.

--- Modifica 1

Fonte 4

In questo video di NgEurope Rob Wormald afferma anche che non è necessario annullare l'iscrizione a Router Observables. Cita anche il httpservizio e ActivatedRoute.paramsin questo video di novembre 2016 .

--- Risposta originale

TLDR:

Per questa domanda ci sono (2) tipi di Observables- valore finito e valore infinito .

http Observablesproduce valori finiti (1) e qualcosa come un DOM event listener Observablesproduce valori infiniti .

Se chiamate manualmente subscribe(non usando la pipa asincrona), quindi unsubscribeda infinito Observables .

Non preoccuparti di quelli finiti , RxJssi prenderanno cura di loro.

Fonte 1

Ho rintracciato una risposta di Rob Wormald in Angular's Gitter qui .

Egli afferma (ho riorganizzato per chiarezza e l'enfasi è mia)

se si tratta di una sequenza a valore singolo (come una richiesta http) la pulizia manuale non è necessaria (supponendo che abbiate sottoscritto manualmente il controller)

dovrei dire "se è una sequenza che completa " (di cui le sequenze a valore singolo, alla http, sono una)

se è una sequenza infinita , dovresti annullare l' iscrizione della pipe asincrona che fa per te

Inoltre menziona in questo video di YouTube su Observables che they clean up after themselves... nel contesto di Observables che complete(come Promises, che completano sempre perché producono sempre 1 valore e terminano - non ci siamo mai preoccupati di annullare l'iscrizione a Promises per assicurarci che ripulissero l' xhrevento ascoltatori, vero?).

Fonte 2

Anche nella guida Rangle a Angular 2 si legge

Nella maggior parte dei casi non avremo bisogno di chiamare esplicitamente il metodo di annullamento dell'iscrizione a meno che non desideriamo annullare anticipatamente o se il nostro osservabile ha una durata maggiore rispetto al nostro abbonamento. Il comportamento predefinito degli operatori osservabili è di disporre dell'abbonamento non appena vengono pubblicati i messaggi .complete () o .error (). Tieni presente che RxJS è stato progettato per essere utilizzato in modo "incendi e dimentica" per la maggior parte del tempo.

Quando si applica la frase our Observable has a longer lifespan than our subscription?

Si applica quando viene creato un abbonamento all'interno di un componente che viene distrutto prima (o non "molto prima") del Observablecompletamento.

Ho letto questo come significato se ci iscriviamo a una httprichiesta o a un osservabile che emette 10 valori e il nostro componente viene distrutto prima che la httprichiesta ritorni o che i 10 valori siano stati emessi, siamo ancora ok!

Quando la richiesta viene restituita o viene finalmente emesso il decimo valore, il processo Observableverrà completato e tutte le risorse verranno ripulite.

Fonte 3

Se guardiamo questo esempio dalla stessa guida di Rangle possiamo vedere che il Subscriptionto route.paramsrichiede un unsubscribe()perché non sappiamo quando paramssmetteranno di cambiare (emettendo nuovi valori).

Il componente potrebbe essere distrutto allontanandosi nel qual caso i parametri del percorso probabilmente continueranno a cambiare (potrebbero cambiare tecnicamente fino al termine dell'app) e le risorse allocate nell'abbonamento verrebbero comunque allocate perché non è presente un completion.


16
La chiamata complete()da sola non sembra ripulire gli abbonamenti. Comunque next()sia, quindi complete()credo, takeUntil()si ferma solo quando viene prodotto un valore, non quando la sequenza è terminata.
Firefly,

3
@seangwright Un rapido test con un membro di tipo Subjectall'interno di un componente e attivandolo ngIfper attivare ngOnInite ngOnDestroymostrare che il soggetto e le sue sottoscrizioni non verranno mai completati o eliminati (collegato un finallyoperatore alla sottoscrizione). Devo chiamare Subject.complete()in ngOnDestroy, in modo che le sottoscrizioni possono pulire dopo loro stessi.
Lars,

4
Il tuo --- Edit 3 è molto perspicace, grazie! Ho solo una domanda di follow-up: se si utilizza l' takeUnitlapproccio, non è necessario annullare l'iscrizione manualmente a eventuali osservabili? È così? Inoltre, perché dobbiamo chiamare next()il ngOnDestroy, perché non chiamare complete()?
uglycode

7
@seangwright Questo è deludente; la caldaia aggiuntiva è fastidiosa.
spongessuck,

4
Modifica 3 discussi nel contesto degli eventi su medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87
HankCa

91

Non è necessario disporre di un sacco di abbonamenti e annullare l'iscrizione manualmente. Usa la combinazione Soggetto e takeUntil per gestire le iscrizioni come un boss:

import { Subject } from "rxjs"
import { takeUntil } from "rxjs/operators"

@Component({
  moduleId: __moduleName,
  selector: "my-view",
  templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
  componentDestroyed$: Subject<boolean> = new Subject()

  constructor(private titleService: TitleService) {}

  ngOnInit() {
    this.titleService.emitter1$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something 1 */ })

    this.titleService.emitter2$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something 2 */ })

    //...

    this.titleService.emitterN$
      .pipe(takeUntil(this.componentDestroyed$))
      .subscribe((data: any) => { /* ... do something N */ })
  }

  ngOnDestroy() {
    this.componentDestroyed$.next(true)
    this.componentDestroyed$.complete()
  }
}

Approccio alternativo , che è stato proposto da @acumartini nei commenti , utilizza takeWhile invece di takeUntil . Potresti preferirlo, ma tieni presente che in questo modo l'esecuzione osservabile non verrà annullata su ngDestroy del tuo componente (ad esempio quando esegui calcoli che richiedono tempo o attendi i dati dal server). Il metodo, che si basa su takeUntil , non presenta questo inconveniente e porta alla cancellazione immediata della richiesta. Grazie a @AlexChe per la spiegazione dettagliata nei commenti .

Quindi ecco il codice:

@Component({
  moduleId: __moduleName,
  selector: "my-view",
  templateUrl: "../views/view-route.view.html"
})
export class ViewRouteComponent implements OnInit, OnDestroy {
  alive: boolean = true

  constructor(private titleService: TitleService) {}

  ngOnInit() {
    this.titleService.emitter1$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something 1 */ })

    this.titleService.emitter2$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something 2 */ })

    // ...

    this.titleService.emitterN$
      .pipe(takeWhile(() => this.alive))
      .subscribe((data: any) => { /* ... do something N */ })
  }

  // Probably, this.alive = false MAY not be required here, because
  // if this.alive === undefined, takeWhile will stop. I
  // will check it as soon, as I have time.
  ngOnDestroy() {
    this.alive = false
  }
}

2
Se usa solo un bool per mantenere lo stato, come far funzionare "takeUntil" come previsto?
Val

6
Penso che ci sia una differenza significativa tra l'utilizzo di takeUntile takeWhile. Il primo annulla l'iscrizione alla fonte osservabile immediatamente quando sparato, mentre il secondo annulla l'iscrizione non appena il valore successivo viene prodotto dalla fonte osservabile. Se la produzione di un valore osservabile dalla sorgente è un'operazione che consuma risorse, la scelta tra i due può andare oltre la preferenza di stile. Vedi il plunk
Alex Che

2
@AlexChe grazie per aver fornito un plunk interessante! Questo è un punto molto valido per l'uso generale di takeUntilvs takeWhile, tuttavia, non per il nostro caso specifico. Quando abbiamo bisogno di annullare l'iscrizione degli ascoltatori alla distruzione dei componenti , stiamo solo verificando il valore booleano come () => alivein takeWhile, quindi non vengono utilizzate operazioni che richiedono tempo / memoria e la differenza è praticamente nello stile (spesso per questo caso specifico).
Metamaker,

2
@metamaker Diciamo che nel nostro componente ci iscriviamo a Observable, che estrae internamente una criptovaluta e lancia un nextevento per ogni moneta estratta, e il mining di una di queste monete richiede un giorno. Con takeUntilannulleremo l'iscrizione al mining di origine Observableimmediatamente una volta ngOnDestroychiamato durante la distruzione del nostro componente. Pertanto, la Observablefunzione di mining è in grado di annullare immediatamente l'operazione durante questo processo.
Alex Che

2
OTOH, se usiamo takeWhile, nel ngOnDestorysettiamo semplicemente la variabile booleana. Ma la Observablefunzione di mining potrebbe ancora funzionare fino a un giorno e solo allora durante la sua nextchiamata si renderà conto che non ci sono abbonamenti attivi e deve essere annullato.
Alex Che

76

La classe di abbonamento ha una caratteristica interessante:

Rappresenta una risorsa usa e getta, come l'esecuzione di un osservabile. Una sottoscrizione ha un metodo importante, annullare la sottoscrizione, che non accetta alcun argomento e elimina semplicemente la risorsa detenuta dalla sottoscrizione.
Inoltre, gli abbonamenti possono essere raggruppati insieme tramite il metodo add (), che collegherà un abbonamento figlio all'attuale abbonamento. Quando un abbonamento viene annullato, anche tutti i suoi figli (e i suoi nipoti) verranno cancellati.

È possibile creare un oggetto di abbonamento aggregato che raggruppa tutti gli abbonamenti. Puoi farlo creando un abbonamento vuoto e aggiungendo abbonamenti ad esso usando il suo add()metodo. Quando il componente viene distrutto, devi solo annullare l'iscrizione alla sottoscrizione aggregata.

@Component({ ... })
export class SmartComponent implements OnInit, OnDestroy {
  private subscriptions = new Subscription();

  constructor(private heroService: HeroService) {
  }

  ngOnInit() {
    this.subscriptions.add(this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes));
    this.subscriptions.add(/* another subscription */);
    this.subscriptions.add(/* and another subscription */);
    this.subscriptions.add(/* and so on */);
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}

1
Sto usando questo approccio. Mi chiedo se sia meglio che usare l'approccio con takeUntil (), come nella risposta accettata ... svantaggi?
Manuel Di Iorio,

1
Nessun inconveniente di cui sono a conoscenza. Non penso che sia meglio, solo diverso.
Steven Liekens,

3
Vedi medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 per ulteriori discussioni sull'approccio ufficiale takeUntilrispetto a questo approccio di raccolta di abbonamenti e chiamate unsubscribe. (Questo approccio mi sembra molto più pulito.)
Josh Kelley

4
Un piccolo vantaggio di questa risposta: non è necessario verificare se this.subscriptionsè null
user2023861

2
Basta evitare il concatenamento di metodi di aggiunta come sub = subsciption.add(..).add(..)perché in molti casi produce risultati imprevisti github.com/ReactiveX/rxjs/issues/2769#issuecomment-345636477
Evgeniy Generalov

32

Alcune delle migliori pratiche relative alle cancellazioni osservabili all'interno dei componenti angolari:

Una citazione da Routing & Navigation

Quando si effettua la sottoscrizione a un osservabile in un componente, è quasi sempre necessario annullare l'iscrizione quando il componente viene distrutto.

Ci sono alcuni osservabili eccezionali in cui ciò non è necessario. Le osservabili ActivatedRoute sono tra le eccezioni.

ActivatedRoute e i suoi osservabili sono isolati dal router stesso. Il router distrugge un componente instradato quando non è più necessario e ActivatedRoute iniettato muore con esso.

Sentiti libero di annullare l'iscrizione comunque. È innocua e mai una cattiva pratica.

E nel rispondere ai seguenti link:

Ho raccolto alcune delle migliori pratiche relative alle cancellazioni osservabili all'interno dei componenti angolari da condividere con te:

  • httpl'annullamento della sottoscrizione osservabile è subordinato e dovremmo considerare gli effetti del "callback callback" in esecuzione dopo la distruzione del componente caso per caso. Sappiamo che angolare annulla e pulisce l' httposservabile stesso (1) , (2) . Mentre questo è vero dal punto di vista delle risorse, racconta solo metà della storia. Diciamo che stiamo parlando di chiamare direttamentehttp dall'interno di un componente e la httprisposta ha richiesto più tempo del necessario, quindi l'utente ha chiuso il componente. Ilsubscribe()l'handler verrà comunque chiamato anche se il componente viene chiuso e distrutto. Ciò può avere effetti collaterali indesiderati e negli scenari peggiori lasciare lo stato dell'applicazione interrotto. Può anche causare eccezioni se il codice nel callback tenta di chiamare qualcosa che è stato appena eliminato. Tuttavia, allo stesso tempo, occasionalmente sono desiderati. Ad esempio, supponiamo che tu stia creando un client di posta elettronica e attivi un suono al termine dell'invio dell'e-mail - beh, vorresti comunque che ciò accadesse anche se il componente è chiuso ( 8 ).
  • Non è necessario annullare l'iscrizione a osservabili completi o con errori. Tuttavia, non vi è alcun danno nel farlo (7) .
  • Utilizzare AsyncPipeil più possibile perché annulla automaticamente l'iscrizione alla distruzione dei componenti.
  • Annullare la sottoscrizione agli ActivatedRouteosservabili come route.paramsse fossero abbonati all'interno di un nidificato (aggiunto all'interno di tpl con il selettore dei componenti) o componente dinamico in quanto possono essere abbonati molte volte finché esiste il componente padre / host. Non c'è bisogno di cancellarsi da loro in altri scenari come menzionato nella citazione sopra dai Routing & Navigationdocumenti.
  • Annullare la sottoscrizione agli osservabili globali condivisi tra i componenti esposti attraverso un servizio angolare, ad esempio poiché possono essere sottoscritti più volte a condizione che il componente sia inizializzato.
  • Non è necessario annullare l'iscrizione agli osservabili interni di un servizio con ambito applicativo poiché questo servizio non viene mai distrutto, a meno che l'intera applicazione non venga distrutta, non esiste alcun motivo reale per annullare l'iscrizione e non vi è alcuna possibilità di perdite di memoria. (6) .

    Nota: per quanto riguarda i servizi con ambito, ovvero i fornitori di componenti, vengono distrutti quando il componente viene distrutto. In questo caso, se ci iscriviamo a qualsiasi osservabile all'interno di questo provider, dovremmo prendere in considerazione l'annullamento dell'iscrizione utilizzando l' OnDestroyhook del ciclo di vita che verrà chiamato quando il servizio viene distrutto, secondo i documenti.
  • Utilizzare una tecnica astratta per evitare qualsiasi pasticcio di codice che può essere causato da cancellazioni. Puoi gestire i tuoi abbonamenti con takeUntil (3) oppure puoi usare questo npm pacchetto menzionato in (4) Il modo più semplice per annullare l'iscrizione a Observables in Angolare .
  • Annullare sempre l'iscrizione a FormGrouposservabili come form.valueChangeseform.statusChanges
  • Annullare sempre l'iscrizione a servizi osservabili Renderer2comerenderer2.listen
  • Cancellati da ogni osservabile altro come un passo di guardia di memoria di perdite fino angolare Docs ci dice esplicitamente che osservabili sono inutili per essere sottoscritte (Controllo emissione: (5) Documentazione per RxJS Annullamento della sottoscrizione (Open) ).
  • Bonus: Usa sempre i modi Angolari per associare eventi come HostListenerse Angular si preoccupa bene di rimuovere i listener di eventi se necessario e previene eventuali perdite di memoria dovute a associazioni di eventi.

Un bel suggerimento finale : se non sai se un osservabile viene automaticamente cancellato / completato o meno, aggiungi un completecallback a subscribe(...)e controlla se viene chiamato quando il componente viene distrutto.


La risposta per il n. 6 non è del tutto corretta. I servizi vengono distrutti e ngOnDestroyvengono chiamati quando il servizio viene fornito a un livello diverso da quello di root, ad esempio fornito esplicitamente in un componente che viene successivamente rimosso. In questi casi è necessario annullare l'iscrizione ai servizi osservabili interni
Drenai,

@Drenai, grazie per il tuo commento e cortesemente non sono d'accordo. Se un componente viene distrutto, il componente, il servizio e l'osservabile saranno tutti GCed e l'annullamento della sottoscrizione sarà inutile in questo caso a meno che non si mantenga un riferimento per l'osservabile ovunque lontano dal componente (che non è logico perdere gli stati del componente a livello globale nonostante l'ambito del servizio al componente)
Mouneer,

Se il servizio distrutto ha un abbonamento a un osservabile appartenente a un altro servizio più in alto nella gerarchia DI, allora GC non si verificherà. Evita questo scenario annullando l'iscrizione ngOnDestroy, che viene sempre chiamata quando i servizi vengono distrutti github.com/angular/angular/commit/…
Drenai,

1
@Drenai, controlla la risposta aggiornata.
Mouneer,

3
@Tim Prima di tutto, Feel free to unsubscribe anyway. It is harmless and never a bad practice.e riguardo alla tua domanda, dipende. Se il componente figlio viene avviato più volte (ad esempio, aggiunto all'interno ngIfo caricato in modo dinamico), è necessario annullare l'iscrizione per evitare l'aggiunta di più abbonamenti allo stesso osservatore. Altrimenti non è necessario. Ma preferisco annullare l'iscrizione all'interno del componente figlio in quanto ciò lo rende più riutilizzabile e isolato da come potrebbe essere utilizzato.
Mouneer,

18

Dipende. Se chiamando someObservable.subscribe(), inizi a trattenere alcune risorse che devono essere liberate manualmente al termine del ciclo di vita del componente, dovresti chiamaretheSubscription.unsubscribe() per evitare perdite di memoria.

Diamo un'occhiata più da vicino ai tuoi esempi:

getHero()restituisce il risultato di http.get(). Se guardi nel codice sorgente angolare 2 , http.get()crea due listener di eventi:

_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);

e chiamando unsubscribe(), è possibile annullare la richiesta e gli ascoltatori:

_xhr.removeEventListener('load', onLoad);
_xhr.removeEventListener('error', onError);
_xhr.abort();

Nota che _xhrè specifico per la piattaforma ma penso che sia sicuro supporre che sia unXMLHttpRequest() caso tuo.

Normalmente, questa è una prova sufficiente per giustificare una unsubscribe()chiamata manuale . Ma secondo questa specifica WHATWG , XMLHttpRequest()è soggetta alla garbage collection dopo che è stata "eseguita", anche se ci sono listener di eventi collegati ad essa. Quindi immagino sia per questo che la guida ufficiale di angular 2 ometteunsubscribe() e permetta a GC di ripulire gli ascoltatori.

Per quanto riguarda il tuo secondo esempio, dipende dall'implementazione di params. Ad oggi, la guida ufficiale angolare non mostra più l'annullamento dell'iscrizione params. Ho esaminato di nuovo src e ho scoperto che paramsè solo un oggetto comportamentale . Poiché non sono stati utilizzati listener o timer di eventi e non sono state create variabili globali, dovrebbe essere sicuro omettereunsubscribe() .

La linea di fondo alla tua domanda è che chiama sempre unsubscribe() come protezione contro la perdita di memoria, a meno che non si sia certi che l'esecuzione dell'osservabile non crei variabili globali, aggiunga listener di eventi, imposti timer o faccia qualsiasi altra cosa che comporti perdite di memoria .

In caso di dubbi, esaminare l'attuazione di tale osservabile. Se l'osservabile ha scritto una logica di pulizia nella sua unsubscribe(), che di solito è la funzione che viene restituita dal costruttore, allora hai buone ragioni per considerare seriamente di chiamare unsubscribe().


6

La documentazione ufficiale di Angular 2 fornisce una spiegazione su quando annullare l'iscrizione e quando può essere tranquillamente ignorata. Dai un'occhiata a questo link:

https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

Cerca il paragrafo con l'intestazione Genitore e figli comunicano tramite un servizio e quindi la casella blu:

Si noti che acquisiamo l'abbonamento e cancelliamo l'iscrizione quando l'AstronautComponent viene distrutto. Questo è un passaggio di protezione da perdita di memoria. Non esiste alcun rischio reale in questa app perché la durata di vita di un AstronautComponent è la stessa della durata dell'app stessa. Ciò non sarebbe sempre vero in un'applicazione più complessa.

Non aggiungiamo questa protezione a MissionControlComponent perché, in quanto genitore, controlla la durata del servizio Mission.

Spero che questo ti aiuta.


3
come componente non sai mai se sei un bambino o no. pertanto dovresti sempre annullare l'iscrizione agli abbonamenti come best practice.
SeriousM,

1
Il punto su MissionControlComponent non è in realtà se si tratti di un genitore o meno, è che il componente stesso fornisce il servizio. Quando MissionControl viene distrutto, lo stesso vale per il servizio e qualsiasi riferimento all'istanza del servizio, quindi non vi è alcuna possibilità di una perdita.
terminato il

6

Basato su: Utilizzo dell'eredità di classe per agganciare al ciclo di vita del componente Angular 2

Un altro approccio generico:

export abstract class UnsubscribeOnDestroy implements OnDestroy {
  protected d$: Subject<any>;

  constructor() {
    this.d$ = new Subject<void>();

    const f = this.ngOnDestroy;
    this.ngOnDestroy = () => {
      f();
      this.d$.next();
      this.d$.complete();
    };
  }

  public ngOnDestroy() {
    // no-op
  }

}

E usa:

@Component({
    selector: 'my-comp',
    template: ``
})
export class RsvpFormSaveComponent extends UnsubscribeOnDestroy implements OnInit {

    constructor() {
        super();
    }

    ngOnInit(): void {
      Observable.of('bla')
      .takeUntil(this.d$)
      .subscribe(val => console.log(val));
    }
}


1
Questo NON funziona correttamente. Si prega di fare attenzione quando si utilizza questa soluzione. Ti stai perdendo una this.componentDestroyed$.next()chiamata come la soluzione accettata da sean sopra ...
philn

4

La risposta (e le variazioni) ufficiali su Modifica n. 3 funziona bene, ma la cosa che mi fa impazzire è la "confusione" della logica aziendale intorno all'iscrizione osservabile.

Ecco un altro approccio che utilizza i wrapper.

Warining: codice sperimentale

Il file iscrivitiAndGuard.ts viene utilizzato per creare una nuova estensione osservabile da avvolgere .subscribe()e al suo interno da avvolgere ngOnDestroy().
L'utilizzo è lo stesso .subscribe(), ad eccezione di un primo parametro aggiuntivo che fa riferimento al componente.

import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';

const subscribeAndGuard = function(component, fnData, fnError = null, fnComplete = null) {

  // Define the subscription
  const sub: Subscription = this.subscribe(fnData, fnError, fnComplete);

  // Wrap component's onDestroy
  if (!component.ngOnDestroy) {
    throw new Error('To use subscribeAndGuard, the component must implement ngOnDestroy');
  }
  const saved_OnDestroy = component.ngOnDestroy;
  component.ngOnDestroy = () => {
    console.log('subscribeAndGuard.onDestroy');
    sub.unsubscribe();
    // Note: need to put original back in place
    // otherwise 'this' is undefined in component.ngOnDestroy
    component.ngOnDestroy = saved_OnDestroy;
    component.ngOnDestroy();

  };

  return sub;
};

// Create an Observable extension
Observable.prototype.subscribeAndGuard = subscribeAndGuard;

// Ref: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
declare module 'rxjs/Observable' {
  interface Observable<T> {
    subscribeAndGuard: typeof subscribeAndGuard;
  }
}

Ecco un componente con due abbonamenti, uno con il wrapper e uno senza. L'unica avvertenza è che deve implementare OnDestroy (con il corpo vuoto se lo si desidera), altrimenti Angular non sa chiamare la versione incartata.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/Rx';
import './subscribeAndGuard';

@Component({
  selector: 'app-subscribing',
  template: '<h3>Subscribing component is active</h3>',
})
export class SubscribingComponent implements OnInit, OnDestroy {

  ngOnInit() {

    // This subscription will be terminated after onDestroy
    Observable.interval(1000)
      .subscribeAndGuard(this,
        (data) => { console.log('Guarded:', data); },
        (error) => { },
        (/*completed*/) => { }
      );

    // This subscription will continue after onDestroy
    Observable.interval(1000)
      .subscribe(
        (data) => { console.log('Unguarded:', data); },
        (error) => { },
        (/*completed*/) => { }
      );
  }

  ngOnDestroy() {
    console.log('SubscribingComponent.OnDestroy');
  }
}

Un plunker demo è qui

Una nota aggiuntiva: Re Edit 3 - La soluzione 'ufficiale', può essere semplificata usando takeWhile () anziché takeUntil () prima degli abbonamenti e un semplice valore booleano anziché un altro osservabile in ngOnDestroy.

@Component({...})
export class SubscribingComponent implements OnInit, OnDestroy {

  iAmAlive = true;
  ngOnInit() {

    Observable.interval(1000)
      .takeWhile(() => { return this.iAmAlive; })
      .subscribe((data) => { console.log(data); });
  }

  ngOnDestroy() {
    this.iAmAlive = false;
  }
}

3

Dato che la soluzione di seangwright (Modifica 3) sembra essere molto utile, ho anche trovato un problema nel comprimere questa funzione nel componente di base e suggerire ad altri compagni di squadra del progetto di ricordare di chiamare super () su ngOnDestroy per attivare questa funzione.

Questa risposta fornisce un modo per liberarsi dalla super chiamata e rendere "componentDestroyed $" un nucleo del componente base.

class BaseClass {
    protected componentDestroyed$: Subject<void> = new Subject<void>();
    constructor() {

        /// wrap the ngOnDestroy to be an Observable. and set free from calling super() on ngOnDestroy.
        let _$ = this.ngOnDestroy;
        this.ngOnDestroy = () => {
            this.componentDestroyed$.next();
            this.componentDestroyed$.complete();
            _$();
        }
    }

    /// placeholder of ngOnDestroy. no need to do super() call of extended class.
    ngOnDestroy() {}
}

E quindi puoi usare questa funzione liberamente per esempio:

@Component({
    selector: 'my-thing',
    templateUrl: './my-thing.component.html'
})
export class MyThingComponent extends BaseClass implements OnInit, OnDestroy {
    constructor(
        private myThingService: MyThingService,
    ) { super(); }

    ngOnInit() {
        this.myThingService.getThings()
            .takeUntil(this.componentDestroyed$)
            .subscribe(things => console.log(things));
    }

    /// optional. not a requirement to implement OnDestroy
    ngOnDestroy() {
        console.log('everything works as intended with or without super call');
    }

}

3

Seguendo la risposta di @seangwright , ho scritto una classe astratta che gestisce gli abbonamenti osservabili "infiniti" nei componenti:

import { OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { Subject } from 'rxjs/Subject';
import { Observable } from 'rxjs/Observable';
import { PartialObserver } from 'rxjs/Observer';

export abstract class InfiniteSubscriberComponent implements OnDestroy {
  private onDestroySource: Subject<any> = new Subject();

  constructor() {}

  subscribe(observable: Observable<any>): Subscription;

  subscribe(
    observable: Observable<any>,
    observer: PartialObserver<any>
  ): Subscription;

  subscribe(
    observable: Observable<any>,
    next?: (value: any) => void,
    error?: (error: any) => void,
    complete?: () => void
  ): Subscription;

  subscribe(observable: Observable<any>, ...subscribeArgs): Subscription {
    return observable
      .takeUntil(this.onDestroySource)
      .subscribe(...subscribeArgs);
  }

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

Per usarlo, basta estenderlo nel componente angolare e chiamare il subscribe()metodo come segue:

this.subscribe(someObservable, data => doSomething());

Accetta anche l'errore e completa i callback come al solito, un oggetto osservatore o non li chiama affatto. Ricorda di chiamare super.ngOnDestroy()se stai implementando anche quel metodo nel componente figlio.

Trova qui un riferimento aggiuntivo di Ben Lesh: RxJS: Non annullare l'iscrizione .


2

Ho provato la soluzione di seangwright (Modifica 3)

Ciò non funziona per Observable creato da timer o intervallo.

Tuttavia, l'ho fatto funzionare utilizzando un altro approccio:

import { Component, OnDestroy, OnInit } from '@angular/core';
import 'rxjs/add/operator/takeUntil';
import { Subject } from 'rxjs/Subject';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/Rx';

import { MyThingService } from '../my-thing.service';

@Component({
   selector: 'my-thing',
   templateUrl: './my-thing.component.html'
})
export class MyThingComponent implements OnDestroy, OnInit {
   private subscriptions: Array<Subscription> = [];

  constructor(
     private myThingService: MyThingService,
   ) { }

  ngOnInit() {
    const newSubs = this.myThingService.getThings()
        .subscribe(things => console.log(things));
    this.subscriptions.push(newSubs);
  }

  ngOnDestroy() {
    for (const subs of this.subscriptions) {
      subs.unsubscribe();
   }
 }
}

2

Mi piacciono le ultime due risposte, ma ho riscontrato un problema se la sottoclasse riferimento "this"inngOnDestroy .

L'ho modificato in questo modo e sembra che abbia risolto il problema.

export abstract class BaseComponent implements OnDestroy {
    protected componentDestroyed$: Subject<boolean>;
    constructor() {
        this.componentDestroyed$ = new Subject<boolean>();
        let f = this.ngOnDestroy;
        this.ngOnDestroy = function()  {
            // without this I was getting an error if the subclass had
            // this.blah() in ngOnDestroy
            f.bind(this)();
            this.componentDestroyed$.next(true);
            this.componentDestroyed$.complete();
        };
    }
    /// placeholder of ngOnDestroy. no need to do super() call of extended class.
    ngOnDestroy() {}
}

è necessario utilizzare la funzione freccia per associare il 'questo':this.ngOnDestroy = () => { f.bind(this)(); this.componentDestroyed$.complete(); };
Damsorian

2

Nel caso in cui sia necessario annullare l'iscrizione, è possibile utilizzare il seguente operatore per il metodo osservabile della pipe

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

export const takeUntilDestroyed = (componentInstance: OnDestroy) => <T>(observable: Observable<T>) => {
  const subjectPropertyName = '__takeUntilDestroySubject__';
  const originalOnDestroy = componentInstance.ngOnDestroy;
  const componentSubject = componentInstance[subjectPropertyName] as Subject<any> || new Subject();

  componentInstance.ngOnDestroy = (...args) => {
    originalOnDestroy.apply(componentInstance, args);
    componentSubject.next(true);
    componentSubject.complete();
  };

  return observable.pipe(takeUntil<T>(componentSubject));
};

può essere usato in questo modo:

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

@Component({ template: '<div></div>' })
export class SomeComponent implements OnInit, OnDestroy {

  ngOnInit(): void {
    const observable = Observable.create(observer => {
      observer.next('Hello');
    });

    observable
      .pipe(takeUntilDestroyed(this))
      .subscribe(val => console.log(val));
  }

  ngOnDestroy(): void {
  }
}

L'operatore esegue il wrapping del metodo ngOnDestroy del componente.

Importante: l'operatore dovrebbe essere l'ultimo nel tubo osservabile.


Questo ha funzionato alla grande, tuttavia l'aggiornamento a Angular 9 sembra ucciderlo. Qualcuno sa perché?
ymerej,

1

Di solito è necessario annullare l'iscrizione quando i componenti vengono distrutti, ma Angular lo gestirà sempre di più mentre andiamo, ad esempio nella nuova versione minore di Angular4, hanno questa sezione per l'instradamento dell'annullamento dell'iscrizione:

Devi annullare l'iscrizione?

Come descritto in ActivatedRoute: lo sportello unico per le informazioni sul percorso della pagina Routing & Navigation, il router gestisce gli osservabili che fornisce e localizza gli abbonamenti. Le sottoscrizioni vengono ripulite quando il componente viene distrutto, proteggendo da perdite di memoria, quindi non è necessario annullare l'iscrizione alla route paramMap Observable.

Anche l'esempio seguente è un buon esempio di Angular per creare un componente e distruggerlo dopo, guarda come il componente implementa OnDestroy, se hai bisogno di OnInit, puoi anche implementarlo nel tuo componente, come gli attrezzi OnInit, OnDestroy

import { Component, Input, OnDestroy } from '@angular/core';  
import { MissionService } from './mission.service';
import { Subscription }   from 'rxjs/Subscription';

@Component({
  selector: 'my-astronaut',
  template: `
    <p>
      {{astronaut}}: <strong>{{mission}}</strong>
      <button
        (click)="confirm()"
        [disabled]="!announced || confirmed">
        Confirm
      </button>
    </p>
  `
})

export class AstronautComponent implements OnDestroy {
  @Input() astronaut: string;
  mission = '<no mission announced>';
  confirmed = false;
  announced = false;
  subscription: Subscription;

  constructor(private missionService: MissionService) {
    this.subscription = missionService.missionAnnounced$.subscribe(
      mission => {
        this.mission = mission;
        this.announced = true;
        this.confirmed = false;
    });
  }

  confirm() {
    this.confirmed = true;
    this.missionService.confirmMission(this.astronaut);
  }

  ngOnDestroy() {
    // prevent memory leak when component destroyed
    this.subscription.unsubscribe();
  }
}

3
Confuso. Che cosa stai dicendo qui? Tu (documenti / note recenti di Angular) sembra dire che Angular se ne occupi e in seguito confermerà che l'annullamento dell'iscrizione è un buon modello. Grazie.
Jamie,

1

Un'altra breve aggiunta alle situazioni sopra menzionate è:

  • Annullare sempre la sottoscrizione, quando nuovi valori nel flusso sottoscritto non sono più necessari o non contano, comporteranno un numero inferiore di trigger e un aumento delle prestazioni in alcuni casi. Casi come componenti in cui i dati / eventi sottoscritti non esistono più o è richiesta una nuova sottoscrizione a un flusso tutto nuovo (aggiornamento, ecc.) È un buon esempio per la disiscrizione.

0

nell'applicazione SPA alla funzione ngOnDestroy (ciclo di vita angolare) Per ogni abbonamento è necessario annullare l' iscrizione . vantaggio => per evitare che lo stato diventi troppo pesante.

ad esempio: in component1:

import {UserService} from './user.service';

private user = {name: 'test', id: 1}

constructor(public userService: UserService) {
    this.userService.onUserChange.next(this.user);
}

in servizio:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

public onUserChange: BehaviorSubject<any> = new BehaviorSubject({});

nel componente2:

import {Subscription} from 'rxjs/Subscription';
import {UserService} from './user.service';

private onUserChange: Subscription;

constructor(public userService: UserService) {
    this.onUserChange = this.userService.onUserChange.subscribe(user => {
        console.log(user);
    });
}

public ngOnDestroy(): void {
    // note: Here you have to be sure to unsubscribe to the subscribe item!
    this.onUserChange.unsubscribe();
}

0

Per gestire l'abbonamento utilizzo una classe "Unsubscriber".

Ecco la classe Unsubscriber.

export class Unsubscriber implements OnDestroy {
  private subscriptions: Subscription[] = [];

  addSubscription(subscription: Subscription | Subscription[]) {
    if (Array.isArray(subscription)) {
      this.subscriptions.push(...subscription);
    } else {
      this.subscriptions.push(subscription);
    }
  }

  unsubscribe() {
    this.subscriptions
      .filter(subscription => subscription)
      .forEach(subscription => {
        subscription.unsubscribe();
      });
  }

  ngOnDestroy() {
    this.unsubscribe();
  }
}

E puoi usare questa classe in qualsiasi componente / servizio / effetto ecc.

Esempio:

class SampleComponent extends Unsubscriber {
    constructor () {
        super();
    }

    this.addSubscription(subscription);
}

0

Puoi usare l'ultima Subscriptionclasse per annullare l'iscrizione a Observable con un codice non così disordinato.

Possiamo farlo con, normal variablema sarà override the last subscriptionsu ogni nuovo abbonamento quindi evitatelo, e questo approccio è molto utile quando si ha a che fare con un numero maggiore di Obseravables e un tipo di Obeservables come BehavoiurSubjecteSubject

Sottoscrizione

Rappresenta una risorsa usa e getta, come l'esecuzione di un osservabile. Una sottoscrizione ha un metodo importante, annullare la sottoscrizione, che non accetta alcun argomento e elimina semplicemente la risorsa detenuta dalla sottoscrizione.

puoi usarlo in due modi,

  • è possibile inviare direttamente l'abbonamento alla matrice di abbonamento

     subscriptions:Subscription[] = [];
    
     ngOnInit(): void {
    
       this.subscription.push(this.dataService.getMessageTracker().subscribe((param: any) => {
                //...  
       }));
    
       this.subscription.push(this.dataService.getFileTracker().subscribe((param: any) => {
            //...
        }));
     }
    
     ngOnDestroy(){
        // prevent memory leak when component destroyed
        this.subscriptions.forEach(s => s.unsubscribe());
      }
    
  • utilizzo add()diSubscription

    subscriptions = new Subscription();
    
    this.subscriptions.add(subscribeOne);
    this.subscriptions.add(subscribeTwo);
    
    ngOnDestroy() {
      this.subscriptions.unsubscribe();
    }
    

A Subscriptionpuò contenere abbonamenti figlio e cancellarli in modo sicuro tutti. Questo metodo gestisce possibili errori (ad es. Se eventuali abbonamenti secondari sono nulli).

Spero che sia di aiuto.. :)


0

Il pacchetto SubSink, una soluzione semplice e coerente per l'annullamento dell'iscrizione

Come nessun altro ha menzionato, voglio raccomandare il pacchetto Subsink creato da Ward Bell: https://github.com/wardbell/subsink#readme .

Lo sto usando su un progetto dove siamo diversi sviluppatori che lo usano tutti. Aiuta molto ad avere un modo coerente che funziona in ogni situazione.


0

Per gli osservabili che si completano direttamente dopo aver emesso il risultato come AsyncSubjecto, ad esempio, osservabili da richieste http e simili, non è necessario annullare l'iscrizione. Non è male chiedere unsubscribe()quelli, ma se l'osservabile è closedil metodo di annullamento dell'iscrizione semplicemente non farà nulla :

if (this.closed) {
  return;
}

Quando hai osservabili di lunga durata che emettono diversi valori nel tempo (come ad esempio a BehaviorSubjecto a ReplaySubject) devi annullare l'iscrizione per evitare perdite di memoria.

Puoi facilmente creare un osservabile che si completa direttamente dopo aver emesso un risultato da osservabili di così lunga vita usando un operatore di tubi. In alcune risposte qui take(1)è menzionata la pipa. Ma preferisco la first()pipa . La differenza take(1)è che sarà:

recapita un EmptyErrorcallback all'errore dell'Osservatore se l'Osservabile viene completato prima dell'invio di qualsiasi notifica successiva.

Un altro vantaggio della prima pipe è che puoi passare un predicato che ti aiuterà a restituire il primo valore che soddisfa determinati criteri:

const predicate = (result: any) => { 
  // check value and return true if it is the result that satisfies your needs
  return true;
}
observable.pipe(first(predicate)).subscribe(observer);

Il primo verrà completato direttamente dopo aver emesso il primo valore (o quando si passa un argomento di funzione al primo valore che soddisfa il proprio predicato), quindi non è necessario annullare l'iscrizione.

A volte non sei sicuro che tu abbia una lunga vita osservabile o no. Non sto dicendo che è una buona pratica, ma potresti quindi aggiungere sempre la firstpipe solo per assicurarti di non dover annullare l'iscrizione manualmente. L'aggiunta di una firstpipe aggiuntiva su un osservabile che emetterà un solo valore non fa male.

Durante lo sviluppo è possibile utilizzare la singlepipe che fallirà se l'origine osservabile emette diversi eventi. Questo può aiutarti a esplorare il tipo di osservabile e se è necessario annullare l'iscrizione o meno.

observable.pipe(single()).subscribe(observer);

L' firste singlesembrano molto simili, entrambi i tubi possono prendere un predicato opzionale, ma le differenze sono importanti e ben riassunte in questo StackOverflow risposta qui :

Primo

Emetterà non appena appare il primo elemento. Completerà subito dopo.

singolo

Fallirà se la fonte osservabile emette diversi eventi.


Nota Ho cercato di essere il più preciso e completo possibile nella mia risposta con riferimenti alla documentazione ufficiale, ma per favore commenta se manca qualcosa di importante ...


-1

--- Aggiorna la soluzione Angular 9 e Rxjs 6

  1. Utilizzo unsubscribenel ngDestroyciclo di vita del componente angolare
class SampleComponent implements OnInit, OnDestroy {
  private subscriptions: Subscription;
  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$.subscribe( ... );
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}
  1. Usando takeUntilin Rxjs
class SampleComponent implements OnInit, OnDestroy {
  private unsubscribe$: new Subject<void>;
  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$
    .pipe(takeUntil(this.unsubscribe$))
    .subscribe( ... );
  }

  ngOnDestroy() {
    this.unsubscribe$.next();
    this.unsubscribe$.complete();
  }
}
  1. per qualche azione che chiami in ngOnInitquel momento accade solo una volta quando il componente init.
class SampleComponent implements OnInit {

  private sampleObservable$: Observable<any>;

  constructor () {}

  ngOnInit(){
    this.subscriptions = this.sampleObservable$
    .pipe(take(1))
    .subscribe( ... );
  }
}

Abbiamo anche asyncpipe. Ma, questo uso sul modello (non nella componente angolare).


Il tuo primo esempio è incompleto.
Paul-Sebastian Manole,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.