Açısal / RxJ'ler `Abonelik'ten ne zaman çıkmam gerekir?


721

Ne zaman saklamalısınız Subscriptionörneklerini ve çağırma işlemi unsubscribe()NgOnDestroy yaşam döngüsü sırasında ve ne zaman sadece onları göz ardı edebilirsiniz?

Tüm abonelikleri kaydetmek, bileşen koduna çok fazla karışıklık getirir.

HTTP İstemci Kılavuzu aşağıdaki gibi abonelikleri yoksayar:

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

Aynı zamanda Güzergah ve Navigasyon Rehberi şunları söylüyor:

Sonunda, başka bir yere gideceğiz. Yönlendirici bu bileşeni DOM'dan kaldıracak ve imha edecektir. Bu olmadan önce kendimizi temizlemeliyiz. Özellikle, Angular bileşeni yok etmeden önce abonelikten çıkmalıyız. Bunun yapılmaması bellek sızıntısına neden olabilir.

Biz dan bizim abonelikten Observableiçinde ngOnDestroyyöntemle.

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
Sanırım Subscriptionüzere s http-requestsonlar sadece dediğimiz gibi göz ardı edilebilir onNextbir kez ve sonra da diyoruz onComplete. Bunun Routeryerine onNexttekrar tekrar çağırır ve asla çağıramaz onComplete(bundan emin değilsiniz ...). Aynı şey Observables'den Events için de geçerlidir. Yani sanırım bunlar olmalı unsubscribed.
Springrbua

1
@ gt6707a Akış, o tamamlamanın herhangi bir gözleminden bağımsız olarak tamamlanır (veya tamamlanmaz). Abonelik işlevine sağlanan geri aramalar (gözlemci) kaynakların tahsis edilip edilmediğini belirlemez. subscribePotansiyel olarak kaynakları yukarı yönde tahsis eden , kendisine yapılan çağrıdır .
seangwright

Yanıtlar:


981

--- Düzenleme 4 - Ek Kaynaklar (2018/09/01)

Açısal Ben Lesh ve Ward Bell'deki Adventures'ın son bir bölümünde, bir bileşende nasıl / ne zaman abonelikten çıkılacağı konuları tartışılıyor. Tartışma yaklaşık 1:05:30 'da başlıyor.

Ward right now there's an awful takeUntil dance that takes a lot of machineryve Shai Reznik'den bahseder Angular handles some of the subscriptions like http and routing.

Yanıt olarak Ben, şu anda Gözlemlenebilirlerin Açısal bileşen yaşam döngüsü olaylarına bağlanmasına izin vermek için tartışmalardan bahsetmektedir ve Ward, bir bileşenin bileşen iç durumu olarak tutulan Gözlemlenebilirlerin ne zaman tamamlanacağını bilmenin bir yolu olarak abone olabileceği bir Gözlemlenebilir yaşam döngüsü olayları önermektedir.

Bununla birlikte, şimdi çoğunlukla çözümlere ihtiyacımız var, işte başka kaynaklar.

  1. takeUntil()RxJs çekirdek ekip üyesi Nicholas Jamieson'dan desen için bir öneri ve onu uygulamaya yardımcı olacak bir tslint kuralı. https://ncjamieson.com/avoiding-takeuntil-leaks/

  2. Bileşen örneğini ( this) parametre olarak alan ve sırasında aboneliği otomatik olarak iptal eden Gözlemlenebilir bir işlecin bulunduğu Hafif npm paketi ngOnDestroy. https://github.com/NetanelBasal/ngx-take-until-destroy

  3. AOT yapıları yapmıyorsanız, yukarıdakilerin biraz daha iyi ergonomi ile başka bir varyasyonu (ancak şimdi hepimiz AOT yapıyor olmalıyız). https://github.com/smnbbrv/ngx-rx-collector

  4. *ngSubscribeEşzamansız boru gibi çalışan ancak şablonunuzda gömülü bir görünüm oluşturan özel yönerge , böylece şablonunuzdaki 'paketlenmemiş' değere başvurabilirsiniz. https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

Nicholas'ın bloguna yaptığı bir açıklamada, aşırı kullanımının, takeUntil()bileşeninizin çok fazla yapmaya çalıştığının ve mevcut bileşenlerinizi Özellik ve Sunum bileşenlerine ayırmanın dikkate alınması gerektiğinin bir işareti olabileceğinden bahsediyorum . Daha sonra | async, Özellik bileşeninden Gözlemlenebilir bileşeninin bir InputSunum bileşenine dönüştürülebilir, bu da hiçbir yerde abonelik gerekmediği anlamına gelir. Bu yaklaşım hakkında daha fazla bilgiyi buradan edinebilirsiniz

--- Düzenleme 3 - 'Resmi' Çözüm (2017/04/09)

Ward Bell ile NGConf'taki bu soru hakkında konuştum (ona doğru olduğunu söylediği bu cevabı bile gösterdim) ama bana Angular için dokümanlar ekibinin yayınlanmamış bu soruya bir çözümü olduğunu söyledi (onaylanmaya çalışsalar da) ). Ayrıca SO cevabımı önümüzdeki resmi tavsiyeyle güncelleyebileceğimi söyledi.

İleriye doğru kullanmamız gereken çözüm , sınıf kodlarında çağrıları olan private ngUnsubscribe = new Subject();tüm bileşenlere bir alan eklemektir ..subscribe()Observable

Sonra diyoruz this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();bizim de ngOnDestroy()yöntemlerle.

Gizli sos ( @metamaker tarafından zaten belirtildiği gibi ), bileşenlerin yok edildiğinde tüm aboneliklerin temizleneceğini garanti edecek takeUntil(this.ngUnsubscribe)her çağrıdan önce aramaktır.subscribe() .

Misal:

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

Not:takeUntil Operatör zincirindeki ara gözlemlenebilir sızıntıları önlemek için operatörü sonuncusu olarak eklemek önemlidir .

--- Düzenleme 2 (2016/12/28)

Kaynak 5

Açısal öğretici, Yönlendirme bölümünde şu şekilde belirtilmektedir: "Yönlendirici sağladığı gözlemlenebilirleri yönetir ve abonelikleri yerelleştirir. Bileşen imha edildiğinde abonelikler temizlenir, bellek sızıntılarına karşı korunur, Güzergah gözlemlenebilir. " - Mark Rajcok

Ward Bell'in tüm bunlara ilişkin açıklamaların eserlerde yer aldığı Yönlendirici Gözlemlerle ilgili Açısal dokümanlar için Github sorunları üzerine bir tartışma .

--- Düzenleme 1

Kaynak 4

NgEurope Rob Wormald'ın bu videosunda Router Observables aboneliğini iptal etmenize gerek olmadığını söylüyor. Ayrıca httphizmetten ve Kasım 2016'danActivatedRoute.params bu videoda bahsediyor .

--- Orijinal Yanıt

TLDR:

Bu soru için (2) türü vardır Observables- sonlu değer ve sonsuz değere.

http Observablessonlu (1) değerler üretir ve DOM gibi bir şey sonsuz değerler event listener Observablesüretir .

El ararsanız subscribe, o zaman (zaman uyumsuz borusunu kullanarak değil) unsubscribedan sonsuzun Observables .

Sonlu olanlar için endişelenme RxJs, onlara bakacak.

Kaynak 1

Ben açısal en Gitter Rob Wormald bir cevap izini burada .

(Açıklık için yeniden düzenledim ve vurgu benim)

Bunu ise tek değerli bir bileşen (bir http isteği gibi) el temizleme gereksizdir (el ile kontrol cihazı abone varsayılarak)

i " tamamlayan bir sekans " demeliyim (tek değer sekansları, bir la http, bir olan)

onun sonsuz dizisi ise , abonelikten gereken zaman uyumsuz boru sizin için yapar hangi

Ayrıca bu youtube videosunda Observables hakkında they clean up after themselves... completeher zaman 1 değer ürettikleri ve bitirdikleri için her zaman tamamlanan Promises gibi - xhrolayı temizlediklerinden emin olmak için Promises'tan abonelikten çıkma konusunda asla endişelenmedik dinleyiciler, değil mi?).

Kaynak 2

Ayrıca açısal 2'ye Rangle rehber okur

Çoğu durumda, erken iptal etmek istemedikçe veya Gözlemlenebilirlik aboneliğimizden daha uzun bir ömre sahip olmadıkça, abonelikten çıkma yöntemini açıkça çağırmamız gerekmeyecektir. Gözlemlenebilir işleçlerin varsayılan davranışı, .complete () veya .error () iletileri yayımlanır yayınlanmaz aboneliği atmaktır. RxJS'nin çoğu zaman "ateşle ve unut" tarzında kullanılmak üzere tasarlandığını unutmayın.

İfade ne zaman our Observable has a longer lifespan than our subscriptionuygulanır?

ObservableTamamlanma işleminden önce (veya çok önce değil) yok edilen bir bileşenin içinde bir abonelik oluşturulduğunda geçerlidir .

Bunu bir değer httpveya 10 değer yayan bir gözlemlenebilirliğe abone olursak ve bu httpistek geri gelmeden ya da 10 değer gönderilmeden önce bileşenimiz yok edilirse anlam olarak okudum , hala iyiyiz!

İstek geri döndüğünde veya 10. değer sonunda verildiğinde, istek Observabletamamlanır ve tüm kaynaklar temizlenir.

Kaynak 3

Biz bakarsak bu örnekte aynı Rangle biz görebilirsiniz rehberlik Subscriptionetmek route.paramsbir gerektirir unsubscribe()olanlar ne zaman biz bilmiyoruz çünkü paramsdeğişen (yeni değerler yayan) duracaktır.

Bileşen uzaklaşarak yok edilebilir, bu durumda rota parametreleri muhtemelen değişecektir (uygulama sona erene kadar teknik olarak değişebilirler) ve a olmadığı için abonelikte ayrılan kaynaklar yine tahsis edilecektir completion.


16
complete()Kendi kendine arama , abonelikleri temizlemiyor gibi görünüyor. Ancak arama next()ve sonra complete(), takeUntil()sadece bir değer üretildiğinde durur inanıyorum , dizi sona erdiğinde değil.
Ateşböceği

3
@seangwright Bir Subjectbileşenin içindeki türden bir üyeyle hızlı bir test ve ngIftetikleyici ngOnInitve ngOnDestroygösterileri ile değiştirerek , öznenin ve aboneliklerinin hiçbir zaman tamamlanmayacağını veya finallyatılmayacağını ( aboneliğe bir -operator bağladıkları). Ben çağırmalıdır Subject.complete()içinde ngOnDestroyabonelikleri sonra kendilerini temizlemek, böylece.
Lars

4
Sizin --- Düzenleme 3 , çok teşekkür anlayışlı! Sadece bir sorum var: takeUnitlYaklaşımı kullanıyorsanız, hiçbir zaman gözlenebilirlerden manuel olarak çıkmamıza gerek yok mu? Durum böyle mi? Dahası, neden çağrısına gerekiyor next()yılında ngOnDestroy, neden sadece deme complete()?
uglycode

7
@seangwright Bu hayal kırıklığı yaratıyor; ilave ısıtıcı plakası sinir bozucu.
spongessuck


91

Çok sayıda aboneliğinizin olması ve aboneliğinizi manuel olarak iptal etmeniz gerekmez. Patron gibi abonelikleri yönetmek için Subject ve takeUntil combo öğelerini kullanın :

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

Yorumlarda @acumartini tarafından önerilen alternatif yaklaşım takeUntil yerine takeWhile kullanır . Bunu tercih edebilirsiniz, ancak Bileşeninizin ngDestroy'unda Gözlenebilir yürütme işleminizin iptal edilmeyeceğini unutmayın (örneğin, zaman alıcı hesaplamalar yaparken veya sunucudan veri beklerken). TakeUntil'e dayanan yöntemin bu dezavantajı yoktur ve isteğin derhal iptal edilmesine yol açar. Yorumlarda ayrıntılı açıklama için @AlexChe'e teşekkürler .

İşte kod:

@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
Devleti korumak için sadece bir bool kullanıyorsa, "takeUntil" in nasıl beklendiği gibi çalışır?
Val

6
Bence takeUntilve arasında önemli bir fark var takeWhile. Birincisi, ateşlendiğinde hemen gözlemlenebilir olan kaynaktan aboneliği iptal ederken, ikincisi yalnızca gözlemlenebilir kaynak tarafından bir sonraki değer üretilir üretilmez. Eğer kaynak tarafından gözlemlenebilir bir değer üretmek kaynak tüketen bir işlemse, ikisi arasında seçim yapmak stil tercihinin ötesine geçebilir. Düşüşe
Alex Che

2
@AlexChe ilginç bir düşme sağladığınız için teşekkürler! Bu genel kullanım için çok geçerli bir nokta takeUntilvs takeWhilebizim belirli bir durum için, ancak, değil. Biz abonelikten dinleyici gerektiğinde bileşen yıkım , sadece gibi boole değerini kontrol ediyoruz () => aliveyılında takeWhilefaaliyete tüketen herhangi bir zaman / bellek kullanılmış ve fark (bu özel durum için, ofc) igili oldukça fazla olduğu değildir, bu yüzden.
metamaker

2
@metamaker Diyelim ki, bileşenimizde Observable, kripto para birimini dahili olarak mayınlayan ve nexther mayınlı madeni para için bir olayı tetikleyen ve bu tür bir madeni parayı madencilik bir gün süren bir aboneye abone oluyoruz . İle takeUntilbiz kaynak madencilik çıkmak olacak Observablehemen sonra ngOnDestroybizim bileşeni imha sırasında denir. Böylece madencilik Observablefonksiyonu bu işlem sırasında çalışmasını derhal iptal edebilir.
Alex Che

2
OTOH, kullanırsak takeWhile, ngOnDestorysadece boole değişkenini ayarladık. Ancak madencilik Observableişlevi hala bir güne kadar çalışabilir ve ancak o zaman nextçağrısı sırasında aktif bir abonelik olmadığını ve iptal edilmesi gerektiğini fark eder.
Alex Che

76

Abonelik sınıfının ilginç bir özelliği vardır:

Bir Gözlemlenebilirin yürütülmesi gibi tek kullanımlık bir kaynağı temsil eder. Aboneliğin, abonelikten çıkma ve argüman almayan ve sadece abonelik tarafından tutulan kaynağı elden çıkaran önemli bir yöntemi vardır.
Ayrıca, abonelikler, geçerli Aboneliğe alt Abonelik ekleyecek olan add () yöntemi ile birlikte gruplandırılabilir. Bir Abonelik aboneliği iptal edildiğinde, tüm çocukları (ve torunları) da aboneliği iptal edilecektir.

Tüm aboneliklerinizi gruplandıran bir toplu Abonelik nesnesi oluşturabilirsiniz. Bunu boş bir Abonelik oluşturarak ve add()yöntemini kullanarak Abonelik ekleyerek yapabilirsiniz . Bileşeniniz yok edildiğinde, yalnızca toplu abonelikten çıkmanız gerekir.

@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
Bu yaklaşımı kullanıyorum. Bunun, kabul edilen yanıtta olduğu gibi takeUntil () ile yaklaşımı kullanmaktan daha iyi olup olmadığını mı merak ediyorsunuz?
Manuel Di Iorio

1
Fark ettiğim dezavantaj yok. Bunun daha iyi olduğunu düşünmüyorum, sadece farklı.
Steven Liekens

3
Abonelik toplama ve arama yaklaşımına karşı resmi yaklaşım hakkında daha fazla tartışma için medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 adresine bakın . (Bu yaklaşım benim için çok daha temiz görünüyor.)takeUntilunsubscribe
Josh Kelley

4
Bu cevabın küçük bir faydası: this.subscriptionsboş olup olmadığını kontrol etmek zorunda değilsiniz
user2023861

2
Sadece sub = subsciption.add(..).add(..)birçok durumda beklenmedik sonuçlar ürettiği için ekleme yöntemlerinin zincirlenmesinden kaçının github.com/ReactiveX/rxjs/issues/2769#issuecomment-345636477
Evgeniy Generalov

32

Açısal bileşenler içindeki gözlemlenebilir abonelikten çıkmalarla ilgili en iyi uygulamalardan bazıları:

Adlı kişinin teklifi Routing & Navigation

Bir bileşende gözlemlenebilir bir öğeye abone olurken, neredeyse her zaman bileşen yok edildiğinde abonelikten çıkmayı ayarlarsınız.

Bunun gerekli olmadığı birkaç istisnai gözlemlenebilir madde vardır. ActivatedRoute gözlemlenebilirleri istisnalar arasındadır.

ActivatedRoute ve gözlemlenebilirleri Yönlendiricinin kendisinden yalıtılmıştır. Yönlendirici, artık ihtiyaç duyulmadığında yönlendirilen bir bileşeni yok eder ve enjekte edilen ActivatedRoute onunla birlikte ölür.

Yine de abonelikten çıkabilirsiniz. Zararsızdır ve asla kötü bir uygulama değildir.

Ve aşağıdaki bağlantılara cevap verirken:

Sizinle paylaşmak için Açısal bileşenler içindeki gözlemlenebilir abonelik iptalleriyle ilgili en iyi uygulamalardan bazılarını topladım:

  • httpgözlemlenebilir abonelik iptali şarttır ve bileşen duruma göre yok edildiğinde 'geri çağrıya abone ol' etkilerini dikkate almalıyız. Biz o açısal abonelikten biliyor ve temizler httpkendisi gözlemlenebilir (1) , (2) . Bu kaynaklar açısından doğru olsa da, hikayenin sadece yarısını anlatıyor. Diyelim ki httpbir bileşenin içinden doğrudan arama yapmaktan bahsediyoruz ve httpyanıtın gerekenden daha uzun sürdüğü için kullanıcı bileşeni kapattı. subscribe()bileşen kapalı ve yok edilmiş olsa bile işleyici çağrılır. Bu istenmeyen yan etkilere neden olabilir ve en kötü senaryolarda uygulama durumunu kırık bırakır. Ayrıca, geri aramadaki kod atılan bir şeyi çağırmaya çalışırsa da istisnalara neden olabilir. Ancak aynı zamanda bazen istenir. Diyelim ki, bir e-posta istemcisi oluşturuyorsunuz ve e-posta gönderme işlemi bittiğinde bir ses tetikliyorsunuz - bileşen kapalı olsa bile bunun olmasını istersiniz ( 8 ).
  • Tamamlanmış veya hata yapan gözlemlenebilir cihazlardan çıkmanıza gerek yoktur. Ancak bunu yapmanın bir zararı yoktur (7) .
  • AsyncPipeMümkün olduğunca kullanın , çünkü bileşen imha sırasında gözlemlenebilir olandan otomatik olarak çıkar.
  • Aboneliğini dan ActivatedRoutegibi gözlenebilir route.paramsuzun ebeveyn / ana bileşeni var olduğu gibi birçok kez abone olabilecek bir iç içe (bileşen seçici ile VUK içine eklendi) veya dinamik bileşen içine Abone olan kullanıcılar. Yukarıdaki alıntıda Routing & Navigationdokümanlar tarafından belirtildiği gibi diğer senaryolarda onlardan çıkmanıza gerek yoktur.
  • Bir Açısal hizmet aracılığıyla açığa çıkan bileşenler arasında paylaşılan, örneğin bileşen başlatıldığı sürece birden çok kez abone olabildikleri için, global gözlenebilirlerden aboneliği iptal edin.
  • Bir uygulama kapsamındaki hizmetin dahili gözlemlenebilirliklerinden çıkmanıza gerek yoktur, çünkü bu hizmet asla yok edilmez, tüm uygulamanız yok edilmedikçe, ondan çıkmak için gerçek bir neden yoktur ve bellek sızıntısı olasılığı yoktur. (6) .

    Not: Kapsamlı hizmetler, yani bileşen sağlayıcılar ile ilgili olarak, bileşen yok edildiğinde bunlar yok edilir. Bu durumda, bu sağlayıcının içindeki herhangi bir gözlemlenebilirliğe abone olursak OnDestroy, dokümanlara göre hizmet imha edildiğinde çağrılacak olan yaşam döngüsü kancasını kullanarak abonelikten çıkmayı düşünmeliyiz .
  • Abonelikten kaynaklanabilecek kod karmaşasını önlemek için soyut bir teknik kullanın. Aboneliklerinizi takeUntil (3) ile yönetebilir veya (4) 'de belirtilen bu npm paketi Angular'daki Gözlenebilirlerden aboneliğinizi iptal etmenin en kolay yolu kullanabilirsiniz .
  • Ve FormGroupgibi gözlemlenebilir cihazlardan daima aboneliği iptal edinform.valueChangesform.statusChanges
  • Her zaman aşağıdaki Renderer2gibi gözlemlenebilir hizmetlerden çıkınrenderer2.listen
  • Açısal Belgeler bize hangi gözlemlenebilirlerin abonelikten çıkarılmasının gereksiz olduğunu söyleyene kadar, gözlemlenebilir diğer tüm işlemlerden bellek sızıntısı koruması adımı listesinden çıkın ( Sorunu kontrol edin: (5) RxJS Abonelikten Çıkarma Belgesi (Açık) ).
  • Bonus: HostListenerGerekirse, olay dinleyicilerini kaldırma konusunda açısal bakımlar gibi olayları bağlamak için her zaman Açısal yollarını kullanın ve olay bağlamaları nedeniyle olası bellek sızıntılarını önleyin.

Güzel bir son ipucu : Bir gözlemlenebilir öğenin otomatik olarak aboneliği iptal edilip edilmediğini / tamamlanmadığını bilmiyorsanız, bir completegeri arama ekleyin subscribe(...)ve bileşen yok edildiğinde çağrılıp çağrılmadığını kontrol edin.


6 numaralı cevap doğru değil. Hizmetler yok edilir ve ngOnDestroyhizmet daha sonra kaldırılacak bir bileşende açıkça sağlanan kök düzeyinden farklı bir düzeyde sağlandığında çağrılır. Bu gibi durumlarda iç gözlemlenebilirlik hizmetlerinden
çıkmalısınız

@Drenai, yorumunuz için teşekkürler ve kibarca kabul etmiyorum. Bir bileşen imha edilirse, bileşen, hizmet ve gözlemlenebilir öğelerin tümü GCed olur ve bu durumda gözlenebilenler için bir referansı bileşenden uzak bir yerde tutmazsanız (Bu, bileşen durumlarını küresel olarak sızdırma mantıklı değildir) bileşeni hizmete dahil olmasına rağmen)
Mouneer

Yok edilen hizmet DI hiyerarşisinde daha yüksek bir başka hizmete ait gözlemlenebilir bir aboneliğe sahipse, GC oluşmaz. ngOnDestroyHizmetlerden yok edildiğinde her zaman çağrılan abonelikten çıkarak
Drenai

1
@Drenai, Güncellenmiş cevabı kontrol edin.
Mouneer

3
@Tim Her şeyden önce Feel free to unsubscribe anyway. It is harmless and never a bad practice.ve sorunuzla ilgili olarak değişir. Alt bileşen birden çok kez başlatılırsa (örneğin, içine eklenir ngIfveya dinamik olarak yüklenir), aynı gözlemciye birden çok abonelik eklemekten kaçınmak için abonelikten çıkmanız gerekir. Aksi takdirde gerek yok. Ancak, alt bileşenin aboneliğini iptal etmeyi tercih ediyorum, çünkü bu daha çok yeniden kullanılabilir ve nasıl kullanılabileceğinden izole ediliyor.
Mouneer

18

Değişir. Arayarak ise someObservable.subscribe(), el Serbest kalan edilmelidir senin bileşenin yaşam döngüsü bittiğinde, o zaman aramalısınız bazı kaynak tutan başlamadan theSubscription.unsubscribe()bellek sızıntısı önlemek için.

Örneklerinize daha yakından bakalım:

getHero()sonucunu döndürür http.get(). Açısal 2 kaynak koduna bakarsanız , http.get()iki olay dinleyicisi oluşturur:

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

ve arayarak unsubscribe(), isteği ve dinleyicileri iptal edebilirsiniz:

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

_xhrPlatforma özel olduğunu unutmayın, ancak XMLHttpRequest()sizin durumunuzda olduğunu varsaymanın güvenli olduğunu düşünüyorum .

Normalde, bu, manuel bir unsubscribe()aramayı garanti etmek için yeterli delildir . Ancak bu WHATWG spesifikasyonuna göre , kendisine XMLHttpRequest()bağlı olay dinleyicileri olsa bile, "tamamlandığında" çöp toplama işlemine tabidir. Bu yüzden sanırım açısal 2 resmi rehber atlar unsubscribe()ve GC'nin dinleyicileri temizlemesine izin verir.

İkinci örneğinize gelince, bu uygulamanın uygulanmasına bağlıdır params. Bugün itibariyle, açısal resmi rehber artık aboneliği iptal etmiyor params. Tekrar src içine baktım ve paramsbunun sadece bir BehaviorSubject olduğunu gördüm . Hiçbir olay dinleyicisi veya zamanlayıcısı kullanılmadığından ve hiçbir global değişken oluşturulmadığından, atlamak güvenli olmalıdır unsubscribe().

Sorunuzun en alt satırı unsubscribe(), gözlemlenebilir uygulamanın yürütülmesinin genel değişkenler oluşturmadığından, olay dinleyicileri eklemediğinden, zamanlayıcıları ayarladığından veya bellek sızıntılarına neden olan başka bir şey yapmadığından emin olmadıkça , her zaman bellek sızıntısına karşı bir koruma olarak adlandırılmasıdır . .

Şüphe duyduğunuzda, gözlemlenebilir olanın uygulanmasına bakın. Gözlemlenebilir unsubscribe(), genellikle yapıcı tarafından döndürülen işlev olan bir temizleme mantığı yazdıysa, aramayı ciddi olarak düşünmek için iyi bir nedeniniz var unsubscribe().


6

Açısal 2 resmi dokümantasyon, ne zaman abonelikten çıkılacağı ve ne zaman güvenli bir şekilde göz ardı edilebileceği hakkında bir açıklama sağlar. Bu bağlantıya bir göz atın:

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

Ebeveyn ve çocuklar bir hizmet ve ardından mavi kutu aracılığıyla iletişim kurarken paragrafı arayın :

AstronotComponent yok edildiğinde aboneliği yakaladığımızdan ve abonelikten çıktığımıza dikkat edin. Bu bir bellek sızıntısı koruma adımıdır. Bu uygulamada gerçek bir risk yoktur, çünkü bir AstronautComponent'in ömrü uygulamanın kendisiyle aynıdır. Bu daha karmaşık bir uygulamada her zaman doğru olmaz.

Bu korumayı MissionControlComponent'e eklemiyoruz çünkü üst öğe olarak MissionService'in ömrünü kontrol ediyor.

Umarım bu sana yardımcı olur.


3
bir bileşen olarak çocuk olup olmadığınızı asla bilemezsiniz. bu nedenle her zaman en iyi uygulama olarak aboneliklerden çıkmalısınız.
SeriousM

1
MissionControlComponent ile ilgili nokta, aslında bir ebeveyn olup olmadığı ile ilgili değildir, bileşenin kendisinin hizmeti sağlamasıdır. MissionControl imha edildiğinde, servis ve servis örneğine yapılan göndermeler de bu nedenle bir sızıntı olasılığı yoktur.
bitiş

6

Dayalı: Açısal 2 bileşenli yaşam döngüsüne kancalamak için Sınıf mirasını kullanma

Başka bir genel yaklaşım:

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
  }

}

Ve kullan :

@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
Bu düzgün çalışmaz. Lütfen bu çözümü kullanırken dikkatli olun. this.componentDestroyed$.next()Yukarıdaki sean tarafından kabul edilen çözüm gibi bir çağrıyı kaçırıyorsunuz ...
philn

4

Resmi Edit # 3 yanıtı (ve varyasyonları) iyi çalışıyor, ancak beni çeken şey, iş mantığının gözlemlenebilir abonelik etrafında 'çamurlaşması'.

Sarmalayıcıları kullanan başka bir yaklaşım.

Warining: deneysel kod

SubscribeAndGuard.ts dosyası , kaydırılacak .subscribe()ve içinde kaydırılacak yeni bir Gözlenebilir uzantı oluşturmak için kullanılır ngOnDestroy().
Bileşene .subscribe()başvuran ek bir ilk parametre dışında kullanımla aynıdır .

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

İşte biri aboneli ve diğeri olmayan iki aboneliğe sahip bir bileşen. Tek uyarı OnDestroy (istenirse boş gövdeli) uygulamalıdır, aksi takdirde Angular sarılmış versiyonu çağırmayı bilmez.

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

Bir demo dalgıç burada

Ek bir not: Yeniden Düzenleme 3 - 'Resmi' Çözüm, aboneliklerden önce takeUntil () yerine takeWhile () ve ngOnDestroy'daki başka bir Gözlemlenebilir yerine basit bir boole kullanılarak basitleştirilebilir.

@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

Seangwright'ın çözümü (Edit 3) çok kullanışlı göründüğünden, bu özelliği temel bileşene paketlemek için bir acı buldum ve bu özelliği etkinleştirmek için ngOnDestroy'da super () çağırmayı hatırlamak için diğer proje takım arkadaşlarına ipucu verdim.

Bu yanıt, süper çağrıdan kurtulmak ve "componentDestroyed $" işlevini temel bileşenin çekirdeği yapmak için bir yol sağlar.

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

Ve sonra bu özelliği özgürce kullanabilirsiniz, örneğin:

@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

@Seangwright tarafından verilen yanıtın ardından, bileşenlerde "sonsuz" gözlemlenebilir abonelikleri işleyen soyut bir sınıf yazdım:

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

Kullanmak için, onu açısal bileşeninizde uzatmanız ve subscribe()yöntemi aşağıdaki gibi çağırmanız yeterlidir:

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

Ayrıca hatayı kabul eder ve her zamanki gibi geri çağrıları, bir gözlemci nesnesini kabul eder veya geri çağrıları kabul etmez. super.ngOnDestroy()Bu yöntemi alt bileşende de uyguluyorsanız, aramayı unutmayın .

Burada Ben Lesh tarafından ek bir referans bulabilirsiniz: RxJS: Aboneliği İptal Etme .


2

Seangwright'ın çözümünü denedim (Düzenle 3)

Bu, zamanlayıcı veya aralık tarafından oluşturulan Gözlemlenebilir için çalışmaz.

Ancak, başka bir yaklaşım kullanarak işe aldım:

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

Son iki yanıtı beğendim, ancak alt sınıfa başvuruda bulunulursa bir sorunla "this"karşılaştım ngOnDestroy.

Bunu bu şekilde değiştirdim ve bu sorunu çözmüş gibi görünüyor.

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

'this' bağlamak için ok işlevini kullanmanız gerekir:this.ngOnDestroy = () => { f.bind(this)(); this.componentDestroyed$.complete(); };
Damsorian

2

Aboneliği iptal etmek gerektiğinde, gözlemlenebilir boru yöntemi için aşağıdaki operatör kullanılabilir

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

şu şekilde kullanılabilir:

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 {
  }
}

Operatör, ngOnDestroy bileşeninin bileşenini sarar.

Önemli: operatör gözlemlenebilir boruda sonuncu olmalıdır.


Bu harika çalıştı, ancak açısal 9'a yükseltmek onu öldürüyor gibi görünüyor. Nedenini bilen var mı?
ymerej

1

Bileşenler yok edildiğinde genellikle aboneliğinizi iptal etmeniz gerekir, ancak Angular gittikçe daha fazla işleyecektir, örneğin Angular4'ün yeni küçük sürümünde, aboneliği iptal etmek için bu bölüme sahiptir:

Abonelikten çıkmanız mı gerekiyor?

Yönlendirme ve Gezinme sayfasının ActivatedRoute: rota bilgileri için tek noktadan alışveriş bölümünde açıklandığı gibi, Yönlendirici sağladığı gözlemlenebilirleri yönetir ve abonelikleri yerelleştirir. Abonelikler, bileşen yok edildiğinde bellek sızıntılarına karşı korunduğundan temizlenir, bu nedenle paramMap Gözlemlenebilir yolundan çıkmanız gerekmez.

Ayrıca aşağıdaki örnek, bir bileşen oluşturmak ve daha sonra onu yok etmek için Angular'dan iyi bir örnektir, bileşenin OnDestroy'u nasıl uyguladığına bakın, onInit'e ihtiyacınız varsa, bunu uygulamanız gibi bileşeninize de uygulayabilirsiniz 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
Şaşkın. Burada ne diyorsun? Siz (Açısal en son dokümanlar / notlar), Angular'ın bunu hallettiğini ve daha sonra abonelikten çıkmanın iyi bir kalıp olduğunu doğrulamak için kullandığınızı söylüyorsunuz. Teşekkürler.
jamie

1

Yukarıda belirtilen durumlara bir başka kısa ekleme:

  • Abonelikten çıkma, abone olunan akıştaki yeni değerler artık gerekli olmadığında veya önemli olmadığında, birkaç durumda daha az sayıda tetikleyici ve performans artışı ile sonuçlanır. Abone olunan verilerin / etkinliğin artık bulunmadığı bileşenler veya tamamen yeni bir akışa yeni bir aboneliğin gerekli olduğu durumlar (yenileme vb.) Abonelikten çıkma için iyi bir örnektir.

0

ngOnDestroy fonksiyonundaki SPA uygulamasında (açısal ömür Döngüsü) Her abone için aboneliğinizi iptal etmeniz gerekir . avantaj => devletin çok ağır olmasını önlemek için.

örneğin: bileşen1'de:

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

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

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

serviste:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

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

bileşen2'de:

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

Aboneliği yönetmek için bir "Unsubscriber" sınıfı kullanıyorum.

İşte Unsubscriber Sınıfı.

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

Ve bu sınıfı herhangi bir bileşen / Hizmet / Efekt vb.

Misal:

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

    this.addSubscription(subscription);
}

0

SubscriptionObservable için dağınık olmayan koddan çıkmak için en son sınıfı kullanabilirsiniz .

Biz ile yapabilirsiniz normal variableama olacak override the last subscriptionböylece önlemek subscribe her yeni üzerinde ve bu yaklaşım Obseravables daha sayı ile ilgileniyor zaman çok yararlıdır ve benzeri Obeservables arasında yazın BehavoiurSubjectveSubject

abone

Bir Gözlemlenebilirin yürütülmesi gibi tek kullanımlık bir kaynağı temsil eder. Abonelikte, abonelikten çıkmayan ve hiçbir argüman almayan ve yalnızca abonelik tarafından tutulan kaynağı kullanan önemli bir yöntem vardır.

bunu iki şekilde kullanabilirsiniz,

  • Aboneliği doğrudan Abonelik Dizisine aktarabilirsiniz

     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());
      }
    
  • kullanarak add()birSubscription

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

A Subscription, alt abonelikleri tutabilir ve güvenli bir şekilde tüm aboneliklerini iptal edebilir. Bu yöntem olası hataları işler (örn. Alt abonelikler boşsa).

Bu yardımcı olur umarım.. :)


0

SubSink paketi, abonelikten çıkma için kolay ve tutarlı bir çözüm

Kimsenin bahsetmediği gibi, Ward Bell tarafından oluşturulan Subsink paketini tavsiye etmek istiyorum: https://github.com/wardbell/subsink#readme .

Bir projede kullanıyorum, hepimiz bunu kullanan birkaç geliştirici olsaydık. Her durumda çalışan tutarlı bir yol elde etmek çok yardımcı olur.


0

Sonucu yayınladıktan hemen sonra AsyncSubjectveya örneğin http isteklerinden gözlemlenebilir gibi tamamlanan gözlemlenebilirler için aboneliğinizi iptal etmeniz gerekmez. Bunları aramak zarar vermez unsubscribe(), ancak gözlemlenebilir closedolan abonelikten çıkma yöntemi basitçe hiçbir şey yapmaz :

if (this.closed) {
  return;
}

Zaman içinde çeşitli değerler (örneğin a BehaviorSubjectveya a gibi ReplaySubject) yayan uzun ömürlü gözlemlenebilir cihazlarınız varsa, bellek sızıntılarını önlemek için aboneliğinizi iptal etmeniz gerekir.

Bir boru operatörü kullanarak bu kadar uzun ömürlü gözlemlenebilirlerden bir sonuç verdikten sonra doğrudan tamamlanan bir gözlemlenebilir oluşturabilirsiniz. Burada bazı cevaplarda take(1)borudan bahsedilmektedir. Ama tercih boru . Aradaki fark şu olacaktır:first()take(1)

EmptyErrorGözlemlenebilir bir sonraki bildirim gönderilmeden önce tamamlanırsa, Gözlemcinin hata geri aramasına bir teslim etme .

İlk borunun bir başka avantajı, belirli kriterleri karşılayan ilk değeri döndürmenize yardımcı olacak bir öngörü geçebilmenizdir:

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

Birincisi, ilk değeri yaydıktan hemen sonra (veya bir işlev bağımsız değişkenini yükleminizi karşılayan ilk değeri iletirken) tamamlanır, böylece abonelikten çıkmanıza gerek kalmaz.

Bazen uzun süre gözlemlenebilir olup olmadığınızdan emin olmazsınız. Ben iyi bir uygulama olduğunu söylemiyorum ama o zaman her zaman firstsadece manuel olarak abonelikten çıkmak gerekmeyecek emin olmak için boru ekleyebilirsiniz . firstGözlenebilir bir cihaza yalnızca bir değer yayan ek bir boru eklemek zarar vermez.

Geliştirme sırasında ,single gözlemlenebilir kaynak birkaç olay yayarsa başarısız olacak boruyu kullanabilirsiniz . Bu, gözlemlenebilir türünü ve abonelikten çıkmanın gerekli olup olmadığını keşfetmenize yardımcı olabilir.

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

firstVe singleher iki boru opsiyonel yüklemi alabilir, çok benzer görünüyor ama farklılıklar önemli ve güzel özetlenmektedir burada bu stackoverflow cevap :

İlk

İlk öğe göründüğünde yayılacaktır. Bundan hemen sonra tamamlanacak.

Tek

Gözlenebilir kaynak birkaç olay yayarsa başarısız olur.


Not Resmi belgelere yapılan atıflarla cevabımda mümkün olduğunca doğru ve eksiksiz olmaya çalıştım, ancak önemli bir şey eksikse lütfen yorum yapın ...


-1

--- Açısal 9 ve Rxjs 6 Çözümünü Güncelleyin

  1. Kullanımı unsubscribede ngDestroyAçısal Bileşeni yaşam döngüsü
class SampleComponent implements OnInit, OnDestroy {
  private subscriptions: Subscription;
  private sampleObservable$: Observable<any>;

  constructor () {}

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

  ngOnDestroy() {
    this.subscriptions.unsubscribe();
  }
}
  1. takeUntilRxjs'de kullanma
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. buna çağırdığınız bazı eylemler için ngOnInit, bileşen başlatıldığında yalnızca bir kez gerçekleşir.
class SampleComponent implements OnInit {

  private sampleObservable$: Observable<any>;

  constructor () {}

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

Ayrıca asyncborumuz var. Ancak, bu şablonda kullanılır (Açısal bileşende değil).


İlk örneğiniz eksik.
Paul-Sebastian Manole
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.