Http yöntemleri ile oluşturulan gözlemlenebilir özelliklerden çıkmak gerekli midir?


211

Bellek sızıntısını önlemek için Açısal 2 http çağrılarından çıkmanız mı gerekiyor?

 fetchFilm(index) {
        var sub = this._http.get(`http://example.com`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilm(json));
            })
            .subscribe(e=>sub.unsubscribe());
            ...


Yanıtlar:


253

Yani cevap hayır, değil. Ng2kendini temizleyecek.

Angular'ın Http XHR arka uç kaynağından Http hizmet kaynağı:

resim açıklamasını buraya girin

Sonucu complete()aldıktan sonra nasıl çalıştığına dikkat edin . Bu aslında tamamlandığında aboneliği iptal ettiği anlamına gelir. Yani bunu kendiniz yapmanız gerekmez.

Doğrulamak için bir test:

  fetchFilms() {
    return (dispatch) => {
        dispatch(this.requestFilms());

        let observer = this._http.get(`${BASE_URL}`)
            .map(result => result.json())
            .map(json => {
                dispatch(this.receiveFilms(json.results));
                dispatch(this.receiveNumberOfFilms(json.count));
                console.log("2 isUnsubscribed",observer.isUnsubscribed);
                window.setTimeout(() => {
                  console.log("3 isUnsubscribed",observer.isUnsubscribed);
                },10);
            })
            .subscribe();
        console.log("1 isUnsubscribed",observer.isUnsubscribed);
    };
}

Beklendiği gibi, sonucu aldıktan ve gözlemlenebilir operatörlerle bitirdikten sonra her zaman otomatik olarak abonelikten çıkarıldığını görebilirsiniz. Bu bir zaman aşımı (3) olur, böylece her şey tamamlandığında ve tamamlandığında gözlemlenebilir durumun durumunu kontrol edebiliriz.

Ve sonuç

resim açıklamasını buraya girin

Yani, Ng2otomatik abonelikten çıkma olarak herhangi bir sızıntı olmaz !

Söz güzel: Bu Observableşekilde kategorize edilir finiteaksine, infinite Observableveri sonsuz akışı DOM gibi yayılan edilebilir olan click, örneğin dinleyici.

TEŞEKKÜRLER, @rubyboy bu konuda yardım için.


17
yanıt gelmeden önce kullanıcının sayfadan ayrıldığı veya kullanıcının görünümden ayrılmadan yanıt gelmediği durumlarda ne olur? Bu bir sızıntıya neden olmaz mı?
Thibs

1
Yukarıdakileri de bilmek istiyorum.
1984

4
@ 1984 Cevap HER ZAMAN ABONELİKTİR. Bu cevap, bu yorumda ortaya çıkan sebepten dolayı tamamen yanlıştır. Uzaklaşan kullanıcı bir bellek bağlantısıdır. Ayrıca, abonelikteki kod kullanıcı uzaklaştıktan sonra çalışırsa hatalara neden olabilir / beklenmeyen yan etkilere neden olabilir. Ben tüm gözlemlenebilirlerde takeWhile (() => this.componentActive) kullanma eğilimindedir ve sadece bir bileşendeki tüm gözlemlenebilirleri temizlemek için bu.componentActive = false in ngOnDestroy ayarlayın.
Lenny

4
Burada gösterilen örnek derin bir açısal özel API'yi kapsamaktadır. diğer herhangi bir özel API ile, haber vermeksizin yükseltme ile değişebilir. Temel kural: Resmi olarak belgelenmemişse, herhangi bir varsayımda bulunmayın. Örneğin, AsynPipe sizin için otomatik olarak abone olduğu / aboneliğinizi iptal ettiği açık belgelerine sahiptir. HttpClient belgeleri bu konuda hiçbir şeyden bahsetmez.
YoussefTaghlabi

1
@YoussefTaghlabi, FYI, resmi açısal dokümantasyon ( angular.io/guide/http ) şunu belirtmektedir: "AsyncPipe sizin için otomatik olarak abone olur (ve abonelikten çıkarılır)." Yani, resmi olarak belgelenmiştir.
Hudgi

96

Siz neden bahsediyorsunuz !!!

Tamam, bu yüzden herhangi bir gözlemlenebilir durumdan çıkmak için iki neden var. Kimse çok önemli ikinci neden hakkında çok fazla konuşmuyor gibi görünmüyor!

1) Kaynakları temizleyin. Diğerlerinin söylediği gibi, bu HTTP gözlemlenebilirleri için önemsiz bir sorundur. Sadece kendini temizleyecek.

2) subscribeİşleyicinin çalıştırılmasını önleyin .

(HTTP için bu aslında tarayıcıdaki isteği de iptal eder - bu yüzden yanıtı okumak için zaman kaybetmez. Ama aslında aşağıdaki ana noktam bir yana.)

2 sayısının alaka düzeyi, abone işleyicinizin ne yaptığına bağlı olacaktır:

Senin Eğer subscribe()işleyici işlevi çağırır ne olursa olsun kapatılırsa istenmeyen veya bertaraf edilir yan etkisi her türlü vardır o zaman abonelikten (veya koşullu mantık ekleyin) yürütülmektedir engellemek için gereklidir.

Birkaç durumu düşünün:

1) Bir giriş formu. Kullanıcı adı ve şifre girip 'Giriş'i tıklayın. Sunucu yavaşsa ve iletişim kutusunu kapatmak için Escape'e basmaya karar verirseniz ne olur? Muhtemelen giriş yapmadığınızı varsayacaksınız, ancak kaçış tuşuna bastıktan sonra http isteği döndüyse, o zaman sahip olduğunuz mantığı yine de uygulayacaksınız. Bu, bir hesap sayfasına yönlendirme, istenmeyen bir giriş çerezi veya belirteç değişkeni ayarlanmasına neden olabilir. Bu muhtemelen kullanıcının beklediği gibi değildir.

2) 'E-posta gönder' formu.

Eğer subscribe'SendEmail' için işleyici tetik a böyle bir şey yapar Eğer istisnalar veya istenmeyen davranışı elde edebilir tanzim edilmiş erişim şey farklı bir sayfaya veya deneme için transfer, animasyon 'E-posta gönderilir.'

Ayrıca unsubscribe()'iptal' anlamına gelmemeye dikkat edin . HTTP mesajı yayına girdikten sonra, unsubscribe()sunucunuza zaten ulaşmışsa HTTP isteğini iptal ETMEZ. Yalnızca size gelen yanıtı iptal eder. Ve e-posta muhtemelen gönderilecektir.

E-postayı doğrudan bir UI bileşeninin içine göndermek için abonelik oluşturursanız, muhtemelen imha sırasında abonelikten çıkmayı istersiniz, ancak e-posta UI olmayan bir merkezi hizmet tarafından gönderiliyorsa, muhtemelen gerek yoktur.

3) Yok edilen / kapatılan açısal bir bileşen. Halen çalışmakta olan http gözlemlenebilirleri, abonelikten çıkmadığınız sürece mantıklarını tamamlar ve çalıştırır onDestroy(). Sonuçların önemsiz olup olmadığı abone işleyicisinde ne yaptığınıza bağlı olacaktır. Artık var olmayan bir şeyi güncellemeye çalışırsanız bir hata alabilirsiniz.

Bazen, bileşen atılırsa isteyebileceğiniz bazı eylemleriniz olabilir ve bazılarında yapmazsınız. Örneğin, gönderilen bir e-posta için bir 'swoosh' sesiniz olabilir. Büyük olasılıkla bileşen kapatılsa bile oynatılmasını istersiniz, ancak bileşen üzerinde bir animasyon çalıştırmayı denerseniz başarısız olur. Bu durumda, aboneliğin içindeki bazı ekstra koşullu mantık çözüm olacaktır - ve http gözlemlenebilirliğini iptal etmek istemezsiniz.

Bu yüzden asıl soruya cevap olarak, bellek sızıntılarını önlemek için bunu yapmanız gerekmez. Ancak, istisnalar atabilecek veya uygulama durumunuzu bozabilecek kod çalıştırılarak istenmeyen yan etkilerin tetiklenmesini önlemek için (sık sık) yapmanız gerekir.

İpucu: Gelişmiş durumlarda yararlı olabilecek Subscriptionbir closedboolean özelliği içerir . HTTP için bu tamamlandığında ayarlanacaktır. Açısal'da bazı durumlarda işleyiciniz tarafından kontrol edilebilecek bir _isDestroyedözellik ayarlamak yararlı olabilir .ngDestroysubscribe

2. İpucu: Birden fazla aboneliği yönetiyorsanız, geçici bir new Subscription()nesne ve add(...)diğer abonelikleri oluşturabilirsiniz; böylece ana abonelikten çıktığınızda, eklenen tüm aboneliklerin de aboneliğini iptal edersiniz.


2
Ayrıca, ham http aşırı yüklenmesini döndüren bir hizmetiniz varsa ve daha sonra abone olmadan önce boru hattınız varsa, o zaman alttaki http gözlemlenebilir değil, yalnızca son gözlemlenebilir olan aboneliğinizi iptal etmeniz gerekir. Aslında doğrudan http için bir Aboneliğiniz bile olmayacak, bu yüzden yapamazsınız.
Simon_Weaver

Aboneliği iptal etmek tarayıcı isteğini iptal etse de, sunucu yine de istek üzerinde işlem yapar. Sunucuyu da işlemi iptal etmek için kullanmanın bir yolu var mı? Çoğu abonelikten çıkma tarafından iptal edilen hızlı bir şekilde http istekleri gönderiyorum. Ancak, sunucu hala istemci tarafından iptal edilen istekler üzerinde çalışır ve bu da meşru isteğin beklemesine neden olur.
bala

@bala, bunun için kendi mekanizmanızı bulmanız gerekir - ki bu RxJS ile bir ilgisi olmazdı. Örneğin, istekleri bir tabloya koyabilir ve arka planda 5 saniye sonra çalıştırabilirsiniz, daha sonra bir şeyin iptal edilmesi gerekiyorsa, eski satırlarda yürütülmesini durduracak bir bayrağı silebilir veya ayarlayabilirsiniz. Tamamen başvurunuzun ne olduğuna bağlı olacaktır. Ancak sunucunun engellediğinden bahsettiğiniz için, aynı anda yalnızca bir isteğe izin verecek şekilde yapılandırılmış olabilir - ancak yine de ne kullandığınıza bağlı olacaktır.
Simon_Weaver

🔥🔥🔥İpucu 2 - sadece bir işlevi çağırarak birden fazla abonelikten çıkmak isteyen herkes için bir PRO-İPUCU, 🔥🔥🔥. NgDestroy'da .add () yöntemini ve .unsubscribe () yöntemini kullanarak ekleyin.
abhay tripathi

23

unsubscribeYöntemin çağrılması, devam eden bir HTTP isteğini iptal etmektir, çünkü bu yöntem aborttemel XHR nesnesindeki istemciyi çağırır ve yükleme ve hata olaylarındaki dinleyicileri kaldırır:

// From the XHRConnection class
return () => {
  _xhr.removeEventListener('load', onLoad);
  _xhr.removeEventListener('error', onError);
  _xhr.abort();
};

Bununla birlikte, unsubscribedinleyicileri kaldırır ... Yani iyi bir fikir olabilir ama tek bir istek için gerekli olduğunu sanmıyorum ;-)

Umarım sana yardımcı olur, Thierry


: | saçma: | durdurmak için bir yol arıyordum ... ama ben merak ediyordum ve ben kodlama olsaydı, bazı senaryoda: i böyle yapardı: y = x.subscribe((x)=>data = x); sonra girişlerde bir kullanıcı değişikliği ve x.subscribe((x)=>cachlist[n] = x); y.unsubscribe()hey bizim sunucu kaynaklarını kullandım, ben kazandım 'Seni uzaklara atma ..... ve diğer senaryolarda: sadece y.stop()her şeyi atma çağrısı
deadManN

16

Abonelik Kaldırma bir olan zorunluluktur bir isterseniz deterministik tüm ağ hızları davranışı.

Bileşen A'nın bir sekmede oluşturulduğunu düşünün - Bir 'GET' isteği göndermek için bir düğmeyi tıklıyorsunuz. Yanıtın geri gelmesi 200 ms sürer. Bu nedenle, makinenin sizden daha hızlı olacağını ve http yanıtının işlendiğini ve sekme kapatılmadan ve A bileşeni yok edilmeden önce tamamlandığını bilerek sekmeyi istediğiniz zaman kapatabilirsiniz.

Çok yavaş bir ağa ne dersiniz? Bir düğmeyi tıklarsanız, 'GET' isteğinin yanıtını alması 10 saniye sürer, ancak 5 saniye içinde sekmeyi kapatmaya karar vermeniz beklenir. Bu, A bileşenini daha sonra çöp toplanacak şekilde yok edecektir. Bir dakika bekle! , abonelikten çıkmadık - şimdi 5 saniye sonra bir yanıt geri geliyor ve yok edilen bileşendeki mantık yürütülecek. Bu icra artık dikkate alındıout-of-context ve çok düşük performans da dahil olmak üzere birçok şeye neden olabilir.

Bu nedenle, en iyi uygulama, takeUntil()bileşen yok edildiğinde http çağrılarını kullanmak ve abonelikten çıkmaktır.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

interface User {
  id: string;
  name: string;
  age: number;
}

@Component({
  selector: 'app-foobar',
  templateUrl: './foobar.component.html',
  styleUrls: ['./foobar.component.scss'],
})
export class FoobarComponent implements OnInit, OnDestroy {
  private user: User = null;
  private destroy$ = new Subject();

  constructor(private http: HttpClient) {}

  ngOnInit() {
    this.http
      .get<User>('api/user/id')
      .pipe(takeUntil(this.destroy$))
      .subscribe(user => {
        this.user = user;
      });
  }

  ngOnDestroy(): void {
    this.destroy$.next();  // trigger the unsubscribe
    this.destroy$.complete(); // finalize & clean up the subject stream
  }
}

Onun BehaviorSubject()yerine kullanmak ve sadece aramak complete()için uygun ngOnDestroy()mu?
Saksham

Yine de aboneliğinizi iptal etmeniz gerekir ... neden BehaviourSubjectfarklı olabilir?
Ben Taliadoros

Sonlu gözlemlenebilir oldukları için HttpClient gözlemlenebilirliklerinden çıkmanıza gerek yoktur, yani bir değer yaydıktan sonra tamamlanırlar.
Yatharth Varshney

Yine de aboneliğinizi iptal etmelisiniz, hataları tost etmek için bir engelleme olduğunu varsayalım, ancak hatayı tetikleyen sayfayı zaten terk ettiniz .. başka bir sayfada bir hata mesajı göreceksiniz .... Ayrıca bir sağlık kontrolü sayfası da düşünün tüm mikro hizmetleri çağırıyor ... vb
Washington Guedes

12

Ayrıca yeni HttpClient modülü ile aynı davranış paketler / ortak / http / src / jsonp.ts


Yukarıdaki kod HttpClient ile, kendi (çağrı .complete () ihtiyacını DO ima birim testinden gibi görünüyor github.com/angular/angular/blob/... )
CleverPatrick

Evet, haklısınız, ancak incelerseniz ( github.com/angular/angular/blob/… ) aynı şeyi görürsünüz. Ana soru ile ilgili olarak, açıkça abonelikten çıkmak için? Vakaların çoğu hayır, çünkü Angular tarafından ele alınacak. Uzun bir yanıtın meydana gelebileceği ve başka bir rotaya taşındığınız veya belki de bir bileşeni yok ettiğiniz bir durum olabilir, bu durumda artık bir istisna yaratan mevcut olmayan bir şeye erişme şansınız vardır.
Ricardo Martínez

8

Otomatik olarak tamamlanan gözlemlenebilir cihazlardan çıkmamalısınız (örn. Http, aramalar). Ama gibi sonsuz gözlemlenebilirlikten çıkmak gerekir Observable.timer().


6

Bir süre test ettikten sonra, belgeleri ve HttpClient'in kaynak kodunu okuyun.

HttpClient: https://github.com/angular/angular/blob/master/packages/common/http/src/client.ts

HttpXhrBackend : https://github.com/angular/angular/blob/master/packages/common/http/src/xhr.ts

HttpClientModule: https://indepth.dev/exploring-the-httpclientmodule-in-angular/

Açısal Üniversite: https://blog.angular-university.io/angular-http/

Bu tür Gözlemlenebilir türler tek değerli akışlardır: HTTP isteği başarılı olursa, bu gözlemlenebilirler yalnızca bir değer yayar ve sonra tamamlanır

Ve abonelikten çıkmak için tüm "Soruna ihtiyacım var" sorununun cevabı?

Değişir. Http çağrısı Memoryleaks bir sorun değil. Sorunlar geri arama işlevlerinizdeki mantıktır.

Örneğin: Yönlendirme veya Giriş.

Aramanız bir giriş aramasıysa, "abonelikten çıkmanız" gerekmez, ancak Kullanıcının sayfadan ayrılıp ayrılmadığından emin olmanız gerekir; kullanıcının yokluğunda yanıtı doğru şekilde ele alırsınız.


this.authorisationService
      .authorize(data.username, data.password)
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
        })

Can sıkıcıdan tehlikeye

Şunu hayal edin, şebeke normalden daha yavaş, çağrı 5 saniye daha uzun sürüyor ve kullanıcı giriş görünümünden ayrılıyor ve bir "destek görünümüne" gidiyor.

Bileşen etkin olmayabilir, ancak abonelik olabilir. Bir yanıt durumunda, kullanıcı aniden yeniden yönlendirilir (handleResponse () uygulamanıza bağlı olarak).

Bu değil iyi .

Ayrıca, kullanıcının henüz giriş yapmadığına inanarak bilgisayardan ayrıldığını hayal edin. Ancak mantık, kullanıcının oturum açmasını sağlar, şimdi bir güvenlik sorununuz var.

Aboneliği iptal etmeden ne yapabilirsiniz?

Aramayı, görünümün mevcut durumuna bağlı yapın:

  public isActive = false;
  public ngOnInit(): void {
    this.isActive = true;
  }

  public ngOnDestroy(): void {
    this.isActive = false;
  }

.pipe(takeWhile(value => this.isActive))Yanıtın yalnızca görünüm etkin olduğunda işlendiğinden emin olmak için kullanıcı .


this.authorisationService
      .authorize(data.username, data.password).pipe(takeWhile(value => this.isActive))
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
        })

Ancak aboneliğin memoryleaks'e neden olmadığından nasıl emin olabilirsiniz?

"TeardownLogic" uygulanırsa oturum açabilirsiniz.

Bir aboneliğin teardownLogic öğesi, alt yazı boş olduğunda veya abonelikten çıkarıldığında çağrılır.


this.authorisationService
      .authorize(data.username, data.password).pipe(takeWhile(value => this.isActive))
      .subscribe((res: HttpResponse<object>) => {
          this.handleLoginResponse(res);
        },
        (error: HttpErrorResponse) => {
          this.messageService.error('Authentication failed');
        },
        () => {
          this.messageService.info('Login has completed');
    }).add(() => {
        // this is the teardown function
        // will be called in the end
      this.messageService.info('Teardown');
    });

Aboneliği iptal etmek zorunda değilsiniz. Mantığınızda aboneliğinizde sorunlara neden olabilecek sorunlar olup olmadığını bilmelisiniz. Ve onlara iyi bak. Çoğu durumda, bu bir sorun olmayacak, ancak otorizasyon gibi kritik görevlerde özellikle, "abonelikten çıkma" veya borulama veya koşullu geri arama işlevleri gibi başka bir mantık olsun, beklenmedik davranışlarla ilgilenmelisiniz.

neden her zaman abonelikten çıkmıyorsunuz?

Koy ya da posta isteğinde bulunduğunuzu düşünün. Sunucu her iki şekilde de iletiyi alır, yalnızca yanıt biraz zaman alır. Aboneliği iptal etmek, yayını geri almaz veya koymaz. Ancak abonelikten çıktığınızda, yanıtı ele alma veya Kullanıcıyı, örneğin İletişim Kutusu veya Tost / Mesaj vb. Yoluyla bilgilendirme şansınız olmaz.

Kullanıcının put / post isteğinin yapılmadığına inanmasına neden olur.

Yani değişir. Tasarım kararınız, bu tür sorunlarla nasıl başa çıkacağınız.


4

Bu makaleyi kesinlikle okumalısınız . Neden http'den bile her zaman abonelikten çıkmanız gerektiğini gösterir .

İsteği oluşturduktan sonra ancak arka uçtan bir yanıt almadan önce bileşeni gereksiz görür ve yok ederseniz, aboneliğiniz bileşene referansı koruyacak ve böylece bellek sızıntılarına neden olma şansı yaratacaktır.

Güncelleme

Yukarıdaki teyit doğru gibi görünüyor, ama yine de, cevap geri geldiğinde http aboneliği yine de yok edildi


1
Evet ve aslında tarayıcınızın ağ sekmesinde kırmızı bir 'iptal' görürsünüz, böylece çalıştığından emin olabilirsiniz.
Simon_Weaver

-2

Gözlenebilir RxJS temel olarak ilişkilidir ve buna göre çalışırsınız. Gözlenebilir ve tamamladığımız hareketi yarattığımızda, gözlenebilir otomatik olarak kapanır ve abonelikten çıkar.

Gözetmenlerle aynı şekilde çalışırlar, ancak oldukça farklı bir düzende. Bileşen imha edilirken aboneliklerini iptal etmek için daha iyi uygulama . Bu. $ manageSubscription.unsubscibe ()

Eğer gözlemlenebilir gibi aşağıdaki sözdizimini oluşturduysak,

** yeni gözle gözlemlenebilir ((gözlemci) => {** // Soğuk durumda gözlemlenebilir hale gelir ** observer.complete () **}) **

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.