कोणीय / RxJs मुझे 'सदस्यता' से सदस्यता समाप्त करनी चाहिए


719

मुझे Subscriptionउदाहरणों को कब संग्रहीत करना चाहिए और unsubscribe()NgOnDestroy जीवन चक्र के दौरान आह्वान करना चाहिए और मैं बस उन्हें कब अनदेखा कर सकता हूं?

सभी सदस्यता को सहेजना घटक कोड में बहुत सारी गड़बड़ी का परिचय देता है।

HTTP क्लाइंट गाइड इस तरह सदस्यता को अनदेखा करता है:

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

एक ही समय में मार्ग और नेविगेशन गाइड का कहना है कि:

आखिरकार, हम कहीं और नेविगेट करेंगे। राउटर इस घटक को DOM से हटा देगा और इसे नष्ट कर देगा। हमें ऐसा होने से पहले खुद को साफ करने की जरूरत है। विशेष रूप से, हमें कोणीय को घटक को नष्ट करने से पहले सदस्यता समाप्त करनी चाहिए। ऐसा करने में विफलता स्मृति रिसाव पैदा कर सकती है।

हम अपने तरीके से अनसब्सक्राइब Observableकरते हैं ngOnDestroy

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

20
मुझे लगता है कि उन्हें अनदेखा Subscriptionकिया http-requestsजा सकता है, क्योंकि वे केवल onNextएक बार कॉल करते हैं और फिर वे कॉल करते हैं onComplete। इसके Routerबजाय onNextबार-बार कॉल करता है और शायद कभी कॉल onCompleteनहीं करता (उस बारे में निश्चित नहीं ...)। उसी Observableसे Events के लिए जाता है । इसलिए मुझे लगता है कि उन्हें होना चाहिए unsubscribed
स्प्रिंगब्रुआ

1
@ gt6707a धारा उस पूर्णता के किसी भी अवलोकन से स्वतंत्र (या पूर्ण नहीं) पूरी होती है। सदस्यता फ़ंक्शन को प्रदान की गई कॉलबैक (पर्यवेक्षक) यह निर्धारित नहीं करती है कि संसाधन आवंटित किए गए हैं या नहीं। यह subscribeस्वयं के लिए कॉल है जो संभावित रूप से संसाधनों का अपस्ट्रीम आवंटित करता है।
शनैः शनैः

जवाबों:


980

--- एडिट 4 - अतिरिक्त संसाधन (2018/09/01)

एंगुलर बेन लेश और वार्ड बेल में एडवेंचर्स के एक हालिया एपिसोड में एक घटक में अनसब्सक्राइब कैसे / कब करना है, इसके बारे में चर्चा करें। चर्चा लगभग 1:05:30 से शुरू होती है।

वार्ड उल्लेख right now there's an awful takeUntil dance that takes a lot of machineryऔर Shai Reznik का उल्लेख है Angular handles some of the subscriptions like http and routing

प्रतिक्रिया में बेन का उल्लेख है कि अभी वेधशालाओं को कोणीय घटक जीवन चक्र की घटनाओं में शामिल होने की अनुमति देने के लिए चर्चाएं हैं और वार्ड जीवनरेखा घटनाओं के एक अवलोकन का सुझाव देता है कि घटक को आंतरिक स्थिति के रूप में वेधशालाओं को पूरा करने के तरीके के रूप में जानने के लिए एक घटक सदस्यता ले सकता है।

उस ने कहा, हमें अब समाधान की आवश्यकता है इसलिए यहां कुछ अन्य संसाधन हैं।

  1. takeUntil()RxJs कोर टीम के सदस्य निकोलस जैमिसन से पैटर्न के लिए एक सिफारिश और इसे लागू करने में मदद करने के लिए एक tslint नियम। https://ncjamieson.com/avoiding-takeuntil-leaks/

  2. लाइटवेट एनपीएम पैकेज जो एक ऑब्जर्वेबल ऑपरेटर को उजागर करता है जो thisएक पैरामीटर उदाहरण ( ) को एक पैरामीटर के रूप में लेता है और स्वचालित रूप से अनसब्सक्राइब करता है ngOnDestroyhttps://github.com/NetanelBasal/ngx-take-until-destroy

  3. यदि आप एओटी बिल्ड नहीं कर रहे हैं (लेकिन हम सभी अब एओटी कर रहे हैं) के साथ थोड़ा बेहतर एर्गोनॉमिक्स के साथ ऊपर का एक और बदलाव। https://github.com/smnbbrv/ngx-rx-collector

  4. कस्टम निर्देश *ngSubscribeजो async पाइप की तरह काम करता है, लेकिन आपके टेम्पलेट में एक एम्बेडेड दृश्य बनाता है ताकि आप अपने पूरे टेम्पलेट में 'अलिखित' मान का उल्लेख कर सकें। https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

मैं निकोलस के ब्लॉग पर एक टिप्पणी में उल्लेख करता हूं कि अति का उपयोग इस बात का takeUntil()संकेत हो सकता है कि आपका घटक बहुत अधिक करने की कोशिश कर रहा है और आपके मौजूदा घटकों को फ़ीचर और प्रस्तुति घटकों में अलग करना चाहिए। तब आप | asyncफ़ीचर घटक Inputसे प्रेज़ेंटेशनल घटक में ऑब्ज़र्वेबल को देख सकते हैं, जिसका अर्थ है कि कहीं भी कोई सदस्यता आवश्यक नहीं है। इस दृष्टिकोण के बारे में यहाँ और पढ़ें

--- संपादन 3 - 'आधिकारिक' समाधान (2017/04/09)

मैंने NGConf में इस सवाल के बारे में वार्ड बेल के साथ बात की थी (मैंने उसे यह उत्तर भी दिखाया था जो उसने कहा था कि वह सही है) लेकिन उसने मुझे बताया कि अंगुलियों के लिए डॉक्स टीम ने इस सवाल का हल खोजा है जो अप्रकाशित है (हालांकि वे इसे मंजूरी मिलने पर काम कर रहे हैं) )। उन्होंने मुझे यह भी बताया कि मैं आगामी आधिकारिक सिफारिश के साथ अपने एसओ उत्तर को अपडेट कर सकता हूं।

हम सभी को आगे बढ़ने के लिए जो उपाय करना चाहिए, वह है उन private ngUnsubscribe = new Subject();सभी घटकों के लिए एक क्षेत्र जोड़ना, जिनके पास अपने वर्ग कोड के भीतर .subscribe()कॉल करने के लिए कॉल है Observable

हम तो this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();हमारे ngOnDestroy()तरीकों में कहते हैं ।

गुप्त सॉस (जैसा कि @metamaker द्वारा पहले ही नोट किया गया है ) takeUntil(this.ngUnsubscribe)हमारे प्रत्येक कॉल से पहले .subscribe()कॉल करना है जो सभी सदस्यता को गारंटी देगा कि घटक नष्ट होने पर साफ हो जाएगा।

उदाहरण:

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

नोट:takeUntil ऑपरेटर श्रृंखला में मध्यवर्ती पर्यवेक्षकों के साथ लीक को रोकने के लिए ऑपरेटर को अंतिम एक के रूप में जोड़ना महत्वपूर्ण है ।

--- संपादित 2 (2016/12/28)

स्रोत 5

कोणीय ट्यूटोरियल, राउटिंग चैप्टर अब निम्नलिखित बताता है: "राउटर अपने द्वारा प्रदान की जाने वाली वेधशालाओं का प्रबंधन करता है और सब्सक्रिप्शन को स्थानीय करता है। घटक के नष्ट होने पर सब्सक्रिप्शन की सफाई हो जाती है, मेमोरी लीक से बचाव होता है, इसलिए हमें सदस्यता समाप्त करने की आवश्यकता नहीं है। मार्ग अवलोकन योग्य है। " - मार्क राजकोक

यहां राउटर वेधशालाओं के बारे में कोणीय डॉक्स के लिए गितुब मुद्दों पर चर्चा की गई है जहां वार्ड बेल का उल्लेख है कि इस सब के लिए स्पष्टीकरण कार्यों में है।

--- संपादित करें 1

स्रोत 4

NgEurope Rob Wormald के इस वीडियो में यह भी कहा गया है कि आपको Router Observables से अनसब्सक्राइब करने की आवश्यकता नहीं है। उन्होंने नवंबर 2016 सेhttp सेवा और ActivatedRoute.paramsइस वीडियो का भी उल्लेख किया ।

--- मूल उत्तर

TLDR:

इस प्रश्न के लिए (2) प्रकार हैं Observables- परिमित मूल्य और अनंत मूल्य।

http Observablesउत्पादन परिमित (1) मूल्य और एक डोम की तरह कुछ अनंत मूल्यों का event listener Observablesउत्पादन ।

आप मैन्युअल रूप से कॉल करते हैं subscribe(async पाइप का उपयोग नहीं), तो unsubscribeसे अनंत Observables

के बारे में चिंता मत करो परिमित हैं, RxJsउनकी देखभाल करेगा।

स्रोत 1

मैंने यहां एंगुलर के गटर में रोब वर्मल्ड के एक जवाब को ट्रैक किया ।

वह कहता है (मैं स्पष्टता के लिए पुनर्गठित हूं और जोर मेरा है)

यदि इसका एकल-मान-अनुक्रम (http अनुरोध की तरह) मैनुअल क्लीनअप अनावश्यक है (मान लें कि आप कंट्रोलर में मैन्युअल रूप से सदस्यता लेते हैं)

मुझे कहना चाहिए "यदि इसका एक अनुक्रम जो पूरा होता है " (जिनमें से एकल मूल्य अनुक्रम, एक ला http, एक हैं)

यदि इसका एक अनंत क्रम है , तो आपको उसे अनसब्सक्राइब कर देना चाहिए जो आपके लिए async पाइप करता है

इसके अलावा वे ऑब्जर्वबल्स पर इस यूट्यूब वीडियो में उल्लेख करते हैं कि they clean up after themselves... वेधशालाओं के संदर्भ में complete(जैसे कि वादे, जो हमेशा पूरे होते हैं क्योंकि वे हमेशा 1 मूल्य और समाप्ति का उत्पादन कर रहे हैं - हम कभी भी वादा करने से इनकार करने के बारे में चिंता नहीं करते हैं कि वे साफ सुथरी xhrघटना को सुनिश्चित करते हैं। श्रोताओं, सही?)।

स्रोत 2

इसके अलावा रैंगल गाइड में कोणीय 2 तक यह पढ़ता है

ज्यादातर मामलों में हमें अनसब्सक्राइब विधि को स्पष्ट रूप से कॉल करने की आवश्यकता नहीं होगी, जब तक कि हम जल्दी रद्द नहीं करना चाहते हैं या हमारे ऑब्जर्वेबल के पास हमारी सदस्यता से अधिक लंबी उम्र है। अवलोकन योग्य ऑपरेटरों का डिफ़ॉल्ट व्यवहार सदस्यता के निपटान के लिए है। जल्द ही .complete () या .error () संदेश प्रकाशित किए जाते हैं। ध्यान रखें कि RxJS को ज्यादातर समय "आग और भूल" फैशन में इस्तेमाल करने के लिए डिज़ाइन किया गया था।

वाक्यांश कब our Observable has a longer lifespan than our subscriptionलागू होता है?

यह तब लागू होता है जब किसी घटक के अंदर एक सदस्यता बनाई जाती है जो पूर्ण होने से पहले नष्ट हो जाती है (या 'बहुत पहले' नहीं) Observable

मैंने इसे अर्थ के रूप में पढ़ा यदि हम एक httpअनुरोध या एक अवलोकन योग्य है जो 10 मानों का उत्सर्जन करता है और हमारे घटक उस httpअनुरोध के वापस आने से पहले नष्ट हो जाते हैं या 10 मानों को उत्सर्जित कर दिया गया है, हम अभी भी ठीक हैं!

जब अनुरोध वापस आ जाता है या 10 वें मूल्य को अंतिम रूप Observableसे पूरा कर लिया जाता है और सभी संसाधनों को साफ कर दिया जाएगा।

स्रोत 3

यदि हम इस उदाहरण को उसी Rangle गाइड से देखते हैं तो हम देख सकते हैं कि Subscriptionकरने की route.paramsआवश्यकता है unsubscribe()क्योंकि हमें नहीं पता है कि कब paramsबदलना बंद हो जाएगा (नए मूल्यों का उत्सर्जन)।

घटक को नेविगेट करके नष्ट किया जा सकता है जिस स्थिति में मार्ग परिमाण अभी भी बदलते रहेंगे (वे तकनीकी रूप से ऐप समाप्त होने तक बदल सकते हैं) और सदस्यता में आवंटित संसाधनों को अभी भी आवंटित किया जाएगा क्योंकि वहाँ कोई नहीं है completion


15
complete()खुद कॉल करके सदस्यता को साफ करने के लिए प्रकट नहीं होता है। हालाँकि, कॉलिंग next()और फिर complete()करता है, मेरा मानना ​​है कि takeUntil()केवल तभी रुकता है जब कोई मूल्य उत्पन्न होता है, न कि जब अनुक्रम समाप्त हो जाता है।
जुगनू

2
@seangwright एक Subjectघटक के अंदर प्रकार के एक सदस्य के साथ एक त्वरित परीक्षण और ngIfट्रिगर ngOnInitऔर ngOnDestroyशो के साथ टॉगल करने के लिए , कि विषय और उसकी सदस्यता कभी भी पूरी नहीं होगी या निस्तारण नहीं किया जाएगा ( finallyसदस्यता के लिए एक सहायक को हुक किया गया)। मैं कॉल करना होगा Subject.complete()में ngOnDestroyहै, तो सदस्यता के लिए खुद के बाद साफ कर सकते हैं।
लार्स

3
आपका --- संपादन 3 बहुत ही सुखद है, धन्यवाद! मेरे पास बस एक फॉलोअप सवाल है: यदि takeUnitlदृष्टिकोण का उपयोग करते हुए , हमें किसी भी वेधशाला से मैन्युअल रूप से सदस्यता समाप्त करने की आवश्यकता नहीं है? क्या यह मामला है? इसके अलावा, हम क्यों कॉल करने की आवश्यकता है next()में ngOnDestroy, क्यों सिर्फ फोन नहीं complete()?
बदसूरत

6
@seangwright यह निराशाजनक है; अतिरिक्त बॉयलरप्लेट कष्टप्रद है।
20

3
संपादित 3 पर घटनाओं के संदर्भ में चर्चा की medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87
HankCa

89

आपको सदस्यता का गुच्छा और मैन्युअल रूप से सदस्यता समाप्त करने की आवश्यकता नहीं है। बॉस की तरह सदस्यता को संभालने के लिए सब्जेक्ट और टेक यूटिल कॉम्बो का उपयोग करें :

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

वैकल्पिक दृष्टिकोण है, जो प्रस्तावित किया गया था टिप्पणी में @acumartini द्वारा , का उपयोग करता है takeWhile के बजाय takeUntil । आप इसे पसंद कर सकते हैं, लेकिन यह ध्यान रखें कि इस तरह आपके ऑब्ज़र्वेबल निष्पादन को आपके घटक के एनडेस्ट्रो पर रद्द नहीं किया जाएगा (जैसे जब आप समय लेने वाली गणना करते हैं या सर्वर से डेटा की प्रतीक्षा करते हैं)। विधि, जो टेक यूटिल पर आधारित है , में यह कमी नहीं है और अनुरोध को तत्काल रद्द कर दिया जाता है। टिप्पणियों में विस्तृत विवरण के लिए @AlexChe का धन्यवाद

तो यहाँ कोड है:

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

1
यदि वह केवल राज्य को बनाए रखने के लिए एक बूल का उपयोग करता है, तो "टेक यूटिल" कैसे काम करता है?
वैल

5
मुझे लगता है कि उपयोग करने takeUntilऔर के बीच एक महत्वपूर्ण अंतर है takeWhile। स्रोत से पूर्व unsubscribes जब तुरंत निकाल दिया तो अवलोकन योग्य है, जबकि बाद के unsubscribes जैसे ही अगले मान स्रोत द्वारा उत्पादित किया जाता है। यदि स्रोत द्वारा मान का उत्पादन किया जा सकता है, तो यह एक संसाधन खपत ऑपरेशन है, दोनों के बीच चयन करना शैली की प्राथमिकता से परे हो सकता है। प्लंक
एलेक्स चे

1
दिलचस्प प्लांक प्रदान करने के लिए @AlexChe धन्यवाद! यह takeUntilबनाम के सामान्य उपयोग के लिए बहुत ही मान्य बिंदु है takeWhile, हालांकि, हमारे विशिष्ट मामले के लिए नहीं। जब हम सदस्यता समाप्त श्रोताओं की जरूरत घटक विनाश पर , हम बस की तरह बूलियन मान की जाँच कर रहे () => aliveमें takeWhileहै, इसलिए किसी भी समय / लेने वाली आपरेशन स्मृति उपयोग नहीं किया जाता और अंतर स्टाइल (ओएफसी, इस विशिष्ट मामले के लिए) के बारे में काफी है।
मेटामेकर

1
@metamaker कहते हैं, हमारे घटक में हम एक सदस्यता लेते हैं Observable, जो आंतरिक रूप से कुछ क्रिप्टो-करेंसी को माइंस करता है और nextहर खनन किए गए सिक्के के लिए एक घटना को फायर करता है , और इस तरह के एक सिक्के को खनन करने में एक दिन लगता है। एक बार हमारे घटक विनाश के दौरान कहा जाता है , takeUntilतो हम स्रोत खनन से Observableतुरंत सदस्यता समाप्त कर देंगे ngOnDestroy। इस प्रकार खनन Observableकार्य इस प्रक्रिया के दौरान तुरंत इसे रद्द करने में सक्षम है।
एलेक्स चे

1
OTOH, यदि हम उपयोग करते हैं takeWhile, तो ngOnDestoryहम बस बूलियन चर सेट करते हैं। लेकिन खनन Observableकार्य अभी भी एक दिन तक काम कर सकता है, और उसके बाद ही nextकॉल के दौरान यह महसूस होगा कि कोई सदस्यता सक्रिय नहीं हैं और इसे रद्द करने की आवश्यकता है।
एलेक्स चे

75

सदस्यता वर्ग में एक दिलचस्प विशेषता है:

डिस्पोजेबल संसाधन का प्रतिनिधित्व करता है, जैसे कि एक ऑब्जर्वेबल का निष्पादन। एक सदस्यता में एक महत्वपूर्ण विधि होती है, सदस्यता समाप्त होती है, जो कोई तर्क नहीं लेती है और सदस्यता द्वारा रखे गए संसाधन का निपटान करती है।
इसके अतिरिक्त, सदस्यता को ऐड () विधि के माध्यम से एक साथ रखा जा सकता है, जो वर्तमान सदस्यता के लिए एक बच्चे की सदस्यता को संलग्न करेगा। जब एक सदस्यता सदस्यता समाप्त हो जाती है, तो उसके सभी बच्चों (और उसके पोते) को भी सदस्यता समाप्त कर दी जाएगी।

आप एक सबग्रिड सब्सक्रिप्शन ऑब्जेक्ट बना सकते हैं जो आपके सभी सब्सक्रिप्शन को समूहीकृत करता है। आप एक रिक्त सदस्यता बनाकर और उसकी add()विधि का उपयोग करके उसमें सदस्यताएँ जोड़कर ऐसा करते हैं । जब आपका घटक नष्ट हो जाता है, तो आपको केवल सकल सदस्यता को बंद करने की आवश्यकता है।

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

मैं इस दृष्टिकोण का उपयोग कर रहा हूं। आश्चर्य है कि अगर यह स्वीकार किए गए उत्तर की तरह टेकऑन्टिल () के साथ दृष्टिकोण का उपयोग करने से बेहतर है .. कमियां?
मैनुअल डि इयोरियो

कोई ऐसी कमियां जिससे मैं वाकिफ हूं। मुझे नहीं लगता कि यह बेहतर है, बस अलग है।
स्टीवन लेकेन्स

2
आधिकारिक दृष्टिकोण बनाम सदस्यता लेने और कॉल करने के इस दृष्टिकोण पर आगे की चर्चा के लिए मध्यम.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 देखें । (यह दृष्टिकोण मुझे बहुत साफ लगता है।)takeUntilunsubscribe
जोश केली

3
इस उत्तर का एक छोटा लाभ: आपको यह जांचने की आवश्यकता नहीं है कि this.subscriptionsक्या अशक्त है
user2023861

1
बस जोड़ने के तरीकों की श्रंखला से बचने की तरह sub = subsciption.add(..).add(..)है क्योंकि कई मामलों में यह अप्रत्याशित परिणाम उत्पन्न github.com/ReactiveX/rxjs/issues/2769#issuecomment-345636477
एव्गेनि Generalov

32

कोणीय घटकों के अंदर वेधशालाओं के बारे में कुछ सर्वोत्तम अभ्यास

से एक उद्धरण Routing & Navigation

जब किसी घटक में एक अवलोकन योग्य होता है, तो घटक के नष्ट होने पर आप हमेशा अनसब्सक्राइब करने की व्यवस्था करते हैं।

कुछ असाधारण वेधशालाएँ हैं जहाँ यह आवश्यक नहीं है। ActivatedRoute वेधशालाएँ अपवादों में से हैं।

ActivatedRoute और इसके वेधशालाएँ राउटर से ही इंसुलेटेड हैं। राउटर एक रूट किए गए घटक को तबाह कर देता है जब इसकी आवश्यकता नहीं होती है और इंजेक्शन एक्टिवेटेड रूट इसके साथ मर जाता है।

वैसे भी सदस्यता समाप्त करने के लिए स्वतंत्र महसूस करें। यह हानिरहित है और कभी भी बुरा व्यवहार नहीं करता है।

और निम्नलिखित लिंक के जवाब में:

मैंने आपके साथ साझा करने के लिए कोणीय घटकों के अंदर वेधशालाओं के बारे में कुछ सर्वोत्तम प्रथाओं का संग्रह किया:

  • httpअवलोकन योग्य सदस्यता रद्द करना सशर्त है और हमें केस आधार के आधार पर घटक के नष्ट होने के बाद चलाए जा रहे 'सब्सक्रिप्शन कॉलबैक' के प्रभावों पर विचार करना चाहिए। हम जानते हैं कि कोणीय अस्पष्ट है और httpअवलोकन योग्य (1) , (2) को साफ करता है । हालांकि यह संसाधनों के दृष्टिकोण से सच है, यह केवल आधी कहानी बताता है। मान लें कि हम httpएक घटक के भीतर से सीधे कॉल करने के बारे में बात कर रहे हैं , और httpप्रतिक्रिया की आवश्यकता से अधिक समय लगा इसलिए उपयोगकर्ता ने घटक को बंद कर दिया। subscribe()यदि घटक बंद और नष्ट हो जाता है, तब भी हैंडलर को बुलाया जाएगा। इससे अनचाहे दुष्प्रभाव हो सकते हैं और खराब स्थिति में एप्लिकेशन स्टेट टूट जाता है। यह अपवाद भी हो सकता है यदि कॉलबैक में कोड कुछ ऐसा कॉल करने का प्रयास करता है जिसे अभी निपटाया गया है। हालांकि एक ही समय में कभी-कभी वे वांछित हैं। जैसे, मान लें कि आप एक ईमेल क्लाइंट बना रहे हैं और जब आप ईमेल भेजते हैं तो आप एक ध्वनि को ट्रिगर करते हैं - अच्छी तरह से आप फिर भी चाहते हैं कि घटक बंद होने पर भी ऐसा हो ( 8 )।
  • पूर्ण या त्रुटि वाले वेधशालाओं से सदस्यता समाप्त करने की कोई आवश्यकता नहीं है। हालांकि, ऐसा करने में कोई बुराई नहीं है (7)
  • AsyncPipeजितना संभव हो उतना उपयोग करें क्योंकि यह घटक विनाश पर अवलोकन से स्वचालित रूप से सदस्यता समाप्त करता है।
  • वे पर्यवेक्षकों से सदस्यता ActivatedRouteछोड़ते हैं जैसे route.paramsकि वे एक नेस्टेड (घटक चयनकर्ता के साथ tpl के अंदर जोड़ा गया) या गतिशील घटक के रूप में सब्सक्राइब किए जाते हैं, क्योंकि वे कई बार सब्सक्राइब किए जा सकते हैं जब तक कि माता-पिता / होस्ट घटक मौजूद हों। Routing & Navigationडॉक्स से ऊपर दिए गए उद्धरण में उल्लिखित अन्य परिदृश्यों में उनसे सदस्यता समाप्त करने की आवश्यकता नहीं है ।
  • घटकों के बीच साझा किए गए वैश्विक वेधशालाओं से सदस्यता समाप्त करें, जो एक कोणीय सेवा के माध्यम से उजागर होते हैं उदाहरण के लिए जब तक कि घटक को आरम्भ किया जाता है तब तक उन्हें कई बार सदस्यता दी जा सकती है।
  • इस सेवा की आंतरिक वेधशालाओं से सदस्यता समाप्त करने की कोई आवश्यकता नहीं है क्योंकि यह सेवा कभी भी नष्ट नहीं होती है, जब तक कि आपका पूरा आवेदन नष्ट नहीं हो जाता है, तब तक इससे सदस्यता समाप्त करने का कोई वास्तविक कारण नहीं है और मेमोरी लीक होने की कोई संभावना नहीं है। (६)

    नोट: scoped सेवाओं के बारे में, अर्थात घटक प्रदाता, घटक नष्ट होने पर वे नष्ट हो जाते हैं। इस स्थिति में, यदि हम इस प्रदाता के अंदर किसी भी अवलोकन योग्य को सदस्यता लेते हैं, तो हमें OnDestroyडॉक्स के अनुसार, जीवनचक्र के हुक का उपयोग करते हुए उसमें से सदस्यता रद्द करने पर विचार करना चाहिए, जिसे सेवा नष्ट होने पर कहा जाएगा।
  • किसी भी कोड गड़बड़ी से बचने के लिए एक अमूर्त तकनीक का उपयोग करें जो अनसब्सक्राइब हो सकता है। आप takeUntil (3) के साथ अपनी सदस्यता का प्रबंधन कर सकते हैं या आप (4) में बताए गए इस npm पैकेज का उपयोग कर सकते हैं ।
  • हमेशा की FormGroupतरह पर्यवेक्षकों से सदस्यता समाप्त करें form.valueChangesऔरform.statusChanges
  • हमेशा की Renderer2तरह सेवा की वेधशालाओं से सदस्यता समाप्त करेंrenderer2.listen
  • हर ऑब्जर्व करने योग्य से एक मेमोरी-रिसाव गार्ड कदम के रूप में अनसब्सक्राइब करें जब तक कि एंगुलर डॉक्स स्पष्ट रूप से हमें नहीं बताता कि कौन से वेधशालाएं अनसब्सक्राइब होना अनावश्यक हैं (चेक इश्यू: (5) आरएक्सजेएस अनसब्सक्राइबिंग (ओपन) के लिए प्रलेखन )।
  • बोनस: हमेशा घटनाओं को बांधने के लिए कोणीय तरीके का उपयोग करें जैसे HostListenerकि जरूरत पड़ने पर ईवेंट श्रोताओं को हटाने के बारे में कोणीय देखभाल करता है और ईवेंट बाइंडिंग के कारण किसी भी संभावित मेमोरी लीक को रोकता है।

एक अच्छा अंतिम टिप : यदि आपको पता नहीं है कि क्या एक अवलोकनीय स्वचालित रूप से सदस्यता समाप्त / पूरा हो रहा है या नहीं, तो completeकॉलबैक को जोड़ें subscribe(...)और जांचें कि क्या घटक के नष्ट होने पर उसे कॉल किया जाता है।


नंबर 6 के लिए जवाब काफी सही नहीं है। सेवाओं को नष्ट कर दिया ngOnDestroyजाता है और उन्हें तब बुलाया जाता है जब सेवा को मूल स्तर के अलावा किसी अन्य स्तर पर प्रदान किया जाता है जैसे कि एक घटक में स्पष्ट रूप से प्रदान किया जाता है जिसे बाद में हटा दिया जाता है। इन मामलों में आपको सेवाओं की आंतरिक
वेधशालाओं

@ डैरेन, आपकी टिप्पणी के लिए धन्यवाद और विनम्रता से मैं सहमत नहीं हूं। यदि कोई घटक नष्ट हो जाता है, तो घटक, सेवा और पालन योग्य सभी GCed हो जाएगा और सदस्यता रद्द करना इस मामले में बेकार होगा जब तक कि आप घटक से दूर अवलोकन के लिए एक संदर्भ नहीं रखते (जो कि घटक को विश्व स्तर पर लीक करने के लिए तर्कसंगत नहीं है) घटक को सेवा देने के बावजूद)
मौनियर

यदि सेवा को नष्ट किया जा रहा है, तो डीआई पदानुक्रम में उच्चतर किसी अन्य सेवा से संबंधित अवलोकन के लिए एक सदस्यता है, तो GC नहीं होगा। इस परिदृश्य से ngOnDestroy
बचकर रहें

1
@ ड्रेनई, अपडेट किए गए उत्तर की जांच करें।
Mouneer

2
@ सबसे पहले, Feel free to unsubscribe anyway. It is harmless and never a bad practice.और आपके प्रश्न के संबंध में, यह निर्भर करता है। यदि बच्चे के घटक को कई बार शुरू किया जाता है (उदाहरण के लिए, अंदर जोड़ा ngIfया गतिशील रूप से लोड किया जा रहा है), तो आपको एक ही पर्यवेक्षक के लिए कई सदस्यताएं जोड़ने से बचने के लिए सदस्यता समाप्त करनी होगी। नहीं तो कोई जरूरत नहीं। लेकिन मैं चाइल्ड कंपोनेंट के अंदर अनसब्सक्राइब करना पसंद करता हूं क्योंकि यह इसे और अधिक पुन: प्रयोज्य बनाता है और इसे कैसे उपयोग किया जा सकता है, इससे अलग किया जाता है।
मौनीर

18

निर्भर करता है। यदि कॉल करने से someObservable.subscribe(), आप कुछ संसाधन को पकड़ना शुरू करते हैं जो आपके घटक के जीवनचक्र के समाप्त होने पर मैन्युअल रूप से मुक्त होना चाहिए, तो आपको theSubscription.unsubscribe()मेमोरी रिसाव को रोकने के लिए कॉल करना चाहिए ।

आइए अपने उदाहरणों पर एक नज़र डालें:

getHero()का परिणाम देता है http.get()। यदि आप कोणीय 2 स्रोत कोड में देखते हैं , तो http.get()दो ईवेंट श्रोता बनाते हैं:

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

और कॉल करके unsubscribe(), आप श्रोताओं के अनुरोध को रद्द कर सकते हैं:

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

ध्यान दें कि _xhrप्लेटफ़ॉर्म विशिष्ट है लेकिन मुझे लगता है कि यह मान लेना सुरक्षित है कि यह XMLHttpRequest()आपके मामले में है।

आम तौर पर, मैन्युअल unsubscribe()कॉल को वारंट करने के लिए यह पर्याप्त सबूत है । लेकिन इस WHATWG कल्पना के अनुसार , XMLHttpRequest()एक बार "किया गया" कचरा संग्रह के अधीन है, भले ही वहाँ घटना श्रोता इससे जुड़े हों। इसलिए मुझे लगता है कि क्यों कोणीय 2 आधिकारिक मार्गदर्शिका चूक जाती है unsubscribe()और जीसी को श्रोताओं को साफ करने देती है।

अपने दूसरे उदाहरण के लिए, यह के कार्यान्वयन पर निर्भर करता है params। आज तक, कोणीय आधिकारिक मार्गदर्शिका अब से सदस्यता रद्द नहीं दिखाती है params। मैंने फिर से src में देखा और पाया कि paramsयह एक BehaviorSubject है । चूंकि कोई घटना श्रोताओं या टाइमर का उपयोग नहीं किया गया था, और कोई वैश्विक चर नहीं बनाया गया था, इसलिए इसे सुरक्षित होना चाहिए unsubscribe()

आपके प्रश्न के लिए लब्बोलुआब यह है कि हमेशा unsubscribe()मेमोरी लीक के खिलाफ एक गार्ड के रूप में कॉल करें , जब तक कि आप निश्चित न हों कि ऑब्जर्वेबल का निष्पादन वैश्विक चर नहीं बनाता है, ईवेंट श्रोताओं को जोड़ें, टाइमर सेट करें, या कुछ और करें जो मेमोरी लीक में परिणाम करता है। ।

जब संदेह हो, तो उस अवलोकन के कार्यान्वयन पर गौर करें। यदि अवलोकनीय ने इसमें कुछ साफ-सुथरे तर्क लिखे हैं unsubscribe(), जो आमतौर पर फ़ंक्शन है जिसे कंस्ट्रक्टर द्वारा लौटाया जाता है, तो आपके पास कॉल करने पर गंभीरता से विचार करने का अच्छा कारण है unsubscribe()


6

कोणीय 2 आधिकारिक दस्तावेज अनसब्सक्राइब करने के लिए और कब इसे सुरक्षित रूप से नजरअंदाज किया जा सकता है के लिए एक स्पष्टीकरण प्रदान करता है। इस लिंक पर एक नजर:

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

शीर्ष लेख के साथ पैराग्राफ को देखें माता-पिता और बच्चे एक सेवा और फिर नीले बॉक्स के माध्यम से संवाद करते हैं:

ध्यान दें कि जब हम एस्ट्रोनॉटकंपोनेंट नष्ट हो जाते हैं तो हम सदस्यता पर कब्जा कर लेते हैं और सदस्यता समाप्त कर लेते हैं। यह एक मेमोरी-लीक गार्ड कदम है। इस ऐप में कोई वास्तविक जोखिम नहीं है क्योंकि एक एस्ट्रोनॉटकंपोनेंट का जीवनकाल ऐप के जीवनकाल के समान ही है। यह हमेशा एक अधिक जटिल अनुप्रयोग में सच नहीं होगा।

हम इस गार्ड को MissionControlComponent में नहीं जोड़ते हैं क्योंकि, माता-पिता के रूप में, यह मिशन सेवा के जीवनकाल को नियंत्रित करता है।

मैं आशान्वित हूं कि इससे आपको सहायता मिलेगी।


3
एक घटक के रूप में आप कभी नहीं जानते कि आप बच्चे हैं या नहीं। इसलिए आपको हमेशा सर्वश्रेष्ठ अभ्यास के रूप में सदस्यता से सदस्यता समाप्त करनी चाहिए।
सीरियस मॉम

1
MissionControlComponent के बारे में बात वास्तव में यह नहीं है कि यह माता-पिता है या नहीं, यह है कि घटक स्वयं सेवा प्रदान करता है। जब मिशनकंट्रोल नष्ट हो जाता है, तो सेवा और सेवा के उदाहरण के संदर्भ में कोई परिवर्तन नहीं होता है, इस प्रकार रिसाव की संभावना नहीं होती है।
Ender

6

के आधार पर: कोणीय 2 घटक जीवनचक्र को हुक करने के लिए कक्षा विरासत का उपयोग करना

एक और सामान्य दृष्टिकोण:

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
  }

}

और उपयोग करें :

@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
यह सही ढंग से काम नहीं करता है। कृपया इस समाधान का उपयोग करते समय सावधान रहें। आप this.componentDestroyed$.next()ऊपर शॉन द्वारा स्वीकृत समाधान की तरह एक कॉल को याद कर रहे हैं ...
12

4

आधिकारिक # 3 उत्तर (और विविधताएं) संपादित करता है, लेकिन अच्छी बात यह है कि जो चीज मुझे मिलती है, वह अवलोकन योग्य सदस्यता के आसपास व्यापार तर्क का 'मैला' है।

यहां रैपर का उपयोग करने का एक और तरीका है।

वार्निंग: प्रयोगात्मक कोड

फ़ाइल subscribeAndGuard.ts को लपेटने के लिए .subscribe()और उसके भीतर लपेटने के लिए एक नया अवलोकन विस्तार बनाने के लिए उपयोग किया जाता है ngOnDestroy()
उपयोग एक ही है .subscribe(), घटक को संदर्भित करने वाले अतिरिक्त पहले पैरामीटर को छोड़कर।

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

यहाँ दो सब्सक्रिप्शन के साथ एक घटक है, एक आवरण के साथ और एक बिना। एकमात्र चेतावनी यह है कि ऑनडेस्ट्रो को लागू करना चाहिए (यदि वांछित हो तो खाली शरीर के साथ), अन्यथा कोणीय लिपटे संस्करण को कॉल करने के लिए नहीं जानता है।

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

एक डेमो प्लंकर यहां है

एक अतिरिक्त नोट: री एडिट 3 - 'ऑफिशियल' सॉल्यूशन, इसे सब्स्क्राइब से पहले टेकवांट () के बजाय टेकविले () के बजाय और एक साधारण बूलियन के बजाय एनजीऑनडेस्ट्रो में एक ऑब्जर्वेबल के रूप में इस्तेमाल करके सरल बनाया जा सकता है।

@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 का समाधान (संपादन 3) बहुत उपयोगी प्रतीत होता है, इसलिए मैंने इस सुविधा को आधार घटक में पैक करने के लिए एक दर्द भी पाया, और इस सुविधा को सक्रिय करने के लिए ngOnDestroy पर सुपर () कॉल करने के लिए याद करने के लिए अन्य प्रोजेक्ट टीम के साथियों को संकेत दिया।

यह उत्तर सुपर कॉल से मुक्त सेट करने का एक तरीका प्रदान करता है, और "कंपोनेंटस्ट्रोएड $" को आधार घटक का एक कोर बनाता है।

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

और फिर आप इस सुविधा का उपयोग उदाहरण के लिए स्वतंत्र रूप से कर सकते हैं:

@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 के जवाब के बाद , मैंने एक सार वर्ग लिखा है जो घटकों में "अनंत" वेधशालाओं की सदस्यता को संभालता है:

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

इसका उपयोग करने के लिए, बस इसे अपने कोणीय घटक में विस्तारित करें और subscribe()विधि को निम्नानुसार कॉल करें :

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

यह हमेशा की तरह त्रुटि और पूर्ण कॉलबैक को भी स्वीकार करता है, एक ऑब्जर्वर ऑब्जेक्ट या कॉलबैक बिल्कुल भी नहीं। कॉल करने के लिए याद रखें super.ngOnDestroy()यदि आप उस घटक को बाल घटक में लागू कर रहे हैं।

यहाँ बेन लेश द्वारा एक अतिरिक्त संदर्भ खोजें: RxJS: अनसब्सक्राइब न करें


2

मैंने seangwright के समाधान की कोशिश की (संपादित 3)

यह ऑब्जर्वेबल के लिए काम नहीं कर रहा है जो टाइमर या अंतराल द्वारा बनाया गया है।

हालाँकि, मैं इसे दूसरे दृष्टिकोण का उपयोग करके काम कर रहा हूँ:

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

मुझे पिछले दो उत्तर पसंद हैं, लेकिन मुझे एक मुद्दे का अनुभव हुआ अगर उपवर्ग में संदर्भित किया "this"गया था ngOnDestroy

मैंने इसे ऐसा करने के लिए संशोधित किया, और ऐसा लगता है कि इसने उस मुद्दे को हल कर दिया है।

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.ngOnDestroy = () => { f.bind(this)(); this.componentDestroyed$.complete(); };
Damsorian

2

यदि सदस्यता समाप्त करने की आवश्यकता है, तो अवलोकन योग्य पाइप विधि के लिए निम्नलिखित ऑपरेटर का उपयोग किया जा सकता है

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

यह इस तरह इस्तेमाल किया जा सकता है:

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

ऑपरेटर घटक के ngOnDestroy विधि को लपेटता है।

महत्वपूर्ण: ऑपरेटर को अवलोकन योग्य पाइप में अंतिम होना चाहिए।


इसने बहुत अच्छा काम किया, हालाँकि कोणीय 9 में अपग्रेड करना इसे मार देता है। किसी को पता है क्यों?
यमराज

1

घटकों के नष्ट हो जाने पर आपको आमतौर पर सदस्यता समाप्त करने की आवश्यकता होती है, लेकिन कोणीय इसे अधिक से अधिक संभालते जा रहे हैं, जैसे कि हम जाते हैं, उदाहरण के लिए Angular4 के नए लघु संस्करण में, उनके पास सदस्यता समाप्त करने के लिए यह अनुभाग है:

क्या आपको सदस्यता समाप्त करने की आवश्यकता है?

जैसा कि ActivatedRoute में वर्णित है: रूटिंग एंड नेविगेशन पृष्ठ के रूट सूचना अनुभाग के लिए वन-स्टॉप-शॉप, राउटर उन पर्यवेक्षकों का प्रबंधन करता है जो इसे प्रदान करता है और सदस्यता को स्थानीय करता है। जब घटक नष्ट हो जाता है, तो मेमोरी लीक से रक्षा करते हुए, सदस्यता को साफ कर दिया जाता है, इसलिए आपको मार्गदर्शी अवलोकन के मार्ग से हटने की आवश्यकता नहीं है।

इसके अलावा नीचे का उदाहरण एक घटक बनाने के लिए कोणीय से एक अच्छा उदाहरण है और इसे नष्ट करने के बाद, यह देखें कि कैसे घटक OnDestroy को लागू करता है, यदि आपको onInit की आवश्यकता है, तो आप इसे अपने घटक में भी लागू कर सकते हैं, जैसे कि औजार 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();
  }
}

2
उलझन में। आप यहाँ क्या कह रहे हैं? आप (कोणीय हाल के डॉक्स / नोट्स) यह कहते प्रतीत होते हैं कि कोणीय इसकी देखभाल करता है और फिर बाद में यह पुष्टि करने के लिए कि सदस्यता समाप्त करना एक अच्छा पैटर्न है। धन्यवाद।
जैमी

1

उपर्युक्त स्थितियों के लिए एक और छोटा अतिरिक्त है:

  • हमेशा सदस्यता समाप्त होने पर, जब सब्स्क्राइब्ड स्ट्रीम में नए मानों की अधिक आवश्यकता नहीं होती है या कोई फर्क नहीं पड़ता है, तो इसके परिणामस्वरूप ट्रिगर की संख्या कम होगी और कुछ मामलों में प्रदर्शन में वृद्धि होगी। ऐसे घटक जैसे कि जहां सब्स्क्राइब्ड डेटा / ईवेंट अधिक मौजूद नहीं है या सभी नई स्ट्रीम के लिए एक नई सदस्यता आवश्यक है (रिफ्रेश इत्यादि) सदस्यता के लिए एक अच्छा उदाहरण है।

0

एनजीऑनडेस्ट्रॉय फ़ंक्शन (कोणीय जीवन चक्र) पर एसपीए आवेदन में प्रत्येक सदस्यता के लिए आपको इसे सदस्यता समाप्त करने की आवश्यकता होती है। लाभ => राज्य को बहुत भारी होने से रोकने के लिए।

उदाहरण के लिए: घटक 1 में:

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

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

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

सेवा में:

import {BehaviorSubject} from 'rxjs/BehaviorSubject';

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

घटक 2 में:

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

सदस्यता से निपटने के लिए मैं "Unsubscriber" वर्ग का उपयोग करता हूं।

यहाँ Unsubscriber Class है।

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

और आप किसी भी घटक / सेवा / प्रभाव आदि में इस वर्ग का उपयोग कर सकते हैं।

उदाहरण:

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

    this.addSubscription(subscription);
}

0

आप Subscriptionइतने गन्दे कोड के साथ ऑब्जर्वेबल के लिए सदस्यता समाप्त करने के लिए नवीनतम कक्षा का उपयोग कर सकते हैं ।

हम ऐसा कर सकते हैं, normal variableलेकिन यह override the last subscriptionहर नई सदस्यता पर होगा ताकि इससे बचें, और यह दृष्टिकोण बहुत उपयोगी है जब आप अधिक संख्या में ऑब्जर्बबल्स के साथ काम कर रहे हों, और इस तरह के ओबेसर्वबल्स के प्रकार BehavoiurSubjectऔरSubject

अंशदान

एक डिस्पोजेबल संसाधन का प्रतिनिधित्व करता है, जैसे कि एक ऑब्जर्वेबल का निष्पादन। एक सदस्यता में एक महत्वपूर्ण विधि होती है, सदस्यता समाप्त होती है, जो कोई तर्क नहीं लेती है और सदस्यता द्वारा आयोजित संसाधन का निपटान करती है।

आप इसे दो तरीकों से उपयोग कर सकते हैं,

  • आप सब्सक्रिप्शन ऐरे को सब्सक्रिप्शन को सीधे पुश कर सकते हैं

     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());
      }
    
  • का उपयोग कर add()केSubscription

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

A Subscriptionबच्चे की सदस्यता ले सकता है और सुरक्षित रूप से उन सभी को अनसब्सक्राइब कर सकता है। यह विधि संभव त्रुटियों को संभालती है (जैसे कि यदि कोई बच्चा सदस्यता शून्य है)।

उम्मीद है की यह मदद करेगा.. :)


0

सबसिंक पैकेज, सदस्यता समाप्त करने के लिए एक आसान और सुसंगत समाधान

जैसा कि किसी और ने भी इसका उल्लेख नहीं किया है, मैं वार्ड बेल द्वारा निर्मित सबसिंक पैकेज की सिफारिश करना चाहता हूं: https://github.com/wardbell/subsink#readme

मैं इसे एक परियोजना पर उपयोग कर रहा हूं हम कई डेवलपर्स थे सभी इसका उपयोग कर रहे हैं। सुसंगत तरीके से काम करने में बहुत मदद मिलती है जो हर स्थिति में काम करता है।


0

वे परिणाम जो सीधे तौर पर निकलते हैं जैसे AsyncSubjectकि उदाहरण के लिए या http अनुरोधों से वेधशालाओं को पूरा करने के बाद पूरा करते हैं और ऐसे में आपको सदस्यता समाप्त करने की आवश्यकता नहीं होती है। यह unsubscribe()उन लोगों के लिए कॉल करने के लिए चोट नहीं करता है , लेकिन अगर अवलोकन योग्य है closedतो सदस्यता समाप्त करने की विधि बस कुछ भी नहीं करेगी :

if (this.closed) {
  return;
}

जब आपके पास लंबे समय तक रहने वाली वेधशालाएं होती हैं जो समय के साथ कई मूल्यों का उत्सर्जन करती हैं (जैसे उदाहरण के लिए BehaviorSubjecta ReplaySubject) तो आपको मेमोरी लीक को रोकने के लिए सदस्यता समाप्त करने की आवश्यकता होती है।

आप आसानी से एक अवलोकन योग्य बना सकते हैं जो पाइप ऑपरेटर का उपयोग करके इतने लंबे समय तक जीवित पर्यवेक्षकों के परिणामस्वरूप सीधे पूरा होता है। कुछ उत्तरों में यहां take(1)पाइप का उल्लेख किया गया है। लेकिन मुझे पाइप पसंद हैfirst() । अंतर take(1)यह है कि यह होगा:

EmptyErrorयदि प्रेक्षक किसी अगली अधिसूचना को भेजने से पहले पूरा हो जाता है, तो प्रेक्षक की त्रुटि कॉलबैक को वितरित करें ।

पहले पाइप का एक और फायदा यह है कि आप एक विधेय पास कर सकते हैं जो आपको पहले मान को वापस करने में मदद करेगा जो कुछ मानदंडों को पूरा करता है:

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

पहला मान पूरा करने के बाद सीधे पूरा होगा (या जब कोई फ़ंक्शन पास करता है तो पहला मान तर्क देता है जो आपके विधेय को संतुष्ट करता है) तो सदस्यता समाप्त करने की कोई आवश्यकता नहीं है।

कभी-कभी आप इस बारे में निश्चित नहीं होते हैं कि आपके पास लंबे समय तक अवलोकन योग्य है या नहीं। मैं यह नहीं कह रहा हूं कि यह अच्छा अभ्यास है, लेकिन आप हमेशा firstपाइप जोड़ सकते हैं यह सुनिश्चित करने के लिए कि आपको मैन्युअल रूप से सदस्यता समाप्त करने की आवश्यकता नहीं होगी। firstएक अवलोकनीय पर एक अतिरिक्त पाइप जोड़ना जो केवल एक मूल्य का उत्सर्जन करेगा, चोट नहीं करता है।

विकास के दौरान आप उस singleपाइप का उपयोग कर सकते हैं जो विफल हो जाएगा यदि स्रोत अवलोकन योग्य कई घटनाओं का उत्सर्जन करता है। यह आपको अवलोकन के प्रकार का पता लगाने में मदद कर सकता है और क्या इससे अनसब्सक्राइब करना आवश्यक है या नहीं।

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

ऐसा लगता है firstऔर singleबहुत समान हैं, दोनों पाइप एक वैकल्पिक विधेय ले सकते हैं लेकिन अंतर महत्वपूर्ण हैं और अच्छी तरह से इस स्टैकओवरफ़्लो में संक्षेपित हैं :

प्रथम

जैसे ही पहला आइटम दिखाई देगा, उत्सर्जन होगा। उसके बाद पूरा करेंगे।

एक

यदि स्रोत अवलोकनीय हो तो कई घटनाओं का उत्सर्जन करेगा।


नोट मैंने आधिकारिक दस्तावेज के संदर्भों के साथ अपने उत्तर में यथासंभव सटीक और पूर्ण होने की कोशिश की, लेकिन कृपया टिप्पणी करें कि क्या कुछ महत्वपूर्ण याद आ रहा है ...


-1

--- अद्यतन कोणीय 9 और आरएक्सजे 6 समाधान

  1. का उपयोग unsubscribeपर ngDestroyकोणीय घटक के जीवन चक्र
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 में उपयोग करना
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. कुछ कार्रवाई के लिए जिसे आप कॉल करते हैं, ngOnInitकेवल एक बार जब घटक init होता है।
class SampleComponent implements OnInit {

  private sampleObservable$: Observable<any>;

  constructor () {}

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

हमारे पास asyncपाइप भी है । लेकिन, यह एक टेम्पलेट पर उपयोग होता है (कोणीय घटक में नहीं)।


आपका पहला उदाहरण अधूरा है।
पॉल-सेबेस्टियन मैनोल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.