Angular / RxJs 언제 구독을 취소해야합니까?


719

NgOnDestroy 수명주기 동안 언제 Subscription인스턴스를 저장 하고 호출 해야 unsubscribe()하며 언제 무시할 수 있습니까?

모든 구독을 저장하면 구성 요소 코드에 많은 혼란이 발생합니다.

HTTP 클라이언트 가이드 는 다음과 같은 구독을 무시합니다.

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

동시에 Route & Navigation Guide 는 다음과 같이 말합니다.

결국 우리는 다른 곳을 탐색 할 것입니다. 라우터는 DOM에서이 구성 요소를 제거하여 삭제합니다. 우리는 그런 일이 일어나기 전에 스스로를 깨끗이해야합니다. 특히 Angular가 구성 요소를 파괴하기 전에 구독을 취소해야합니다. 그렇지 않으면 메모리 누수가 발생할 수 있습니다.

우리 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
한 번만 호출 한 다음을 호출하기 때문에 Subscriptions를 http-requests무시할 수 있다고 생각 onNext합니다 onComplete. 는 Router대신 호출 onNext반복적으로 호출하지 않을 수도 있습니다 onComplete(하지 않도록 ... 그것에 대해). Observable에서 Events도 마찬가지입니다 . 그래서 그럴 것 같아요 unsubscribed.
Springrbua

1
@ gt6707a 스트림은 해당 완료에 대한 관찰과 상관없이 완료됩니다 (또는 완료되지 않습니다). 구독 기능에 제공된 콜백 (관찰자)은 자원이 할당되는지 여부를 결정하지 않습니다. subscribe잠재적으로 리소스를 업스트림에 할당하는 것은 그 자체에 대한 요구 입니다.
seangwright

답변:


980

--- 편집 4-추가 리소스 (2018/09/01)

Angular Ben Lesh와 Ward Bell 의 최근 모험 에피소드 에서 구성 요소를 구독 취소하는 방법 / 언제와 관련된 문제에 대해 설명합니다. 토론은 약 1:05:30에 시작됩니다.

와드 언급 right now there's an awful takeUntil dance that takes a lot of machinery과 샤이 레즈 닉 언급 Angular handles some of the subscriptions like http and routing.

이에 대한 응답으로 Ben은 Observables가 Angular 구성 요소 수명주기 이벤트에 연결될 수 있도록하기위한 논의가 있다고 언급했으며 Ward는 구성 요소 내부 상태로 유지되는 Observable을 완료 할시기를 알기위한 방법으로 구성 요소가 가입 할 수있는 Observable 수명주기 이벤트를 제안합니다.

즉, 현재 대부분 솔루션이 필요하므로 여기에 다른 리소스가 있습니다.

  1. takeUntil()RxJs 핵심 팀 멤버 Nicholas Jamieson 의 패턴에 대한 권장 사항 과이를 강화하는 데 도움이되는 tslint 규칙. https://ncjamieson.com/avoiding-takeuntil-leaks/

  2. 구성 요소 인스턴스 ( this)를 매개 변수로 사용하고 자동으로 구독을 취소 하는 Observable 연산자를 제공하는 경량 npm 패키지 ngOnDestroy. https://github.com/NetanelBasal/ngx-take-until-destroy

  3. AOT 빌드를하지 않으면 위의 또 다른 변형이 약간 더 인체 공학적입니다 (그러나 우리는 모두 AOT를해야합니다). https://github.com/smnbbrv/ngx-rx-collector

  4. *ngSubscribe비동기 파이프처럼 작동하지만 템플릿에 포함 된 뷰를 생성하여 템플릿 전체에서 '포장 해제'값을 참조 할 수있는 사용자 지정 지시문 . https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f

필자는 Nicholas의 블로그에 대한 과다 사용은 takeUntil()구성 요소가 너무 많은 노력을 기울이고 있고 기존 구성 요소를 FeaturePresentational 구성 요소로 분리해야한다는 신호일 수 있다고 언급했습니다 . 그런 다음 | asyncFeature 구성 요소 Input에서 Presentational 구성 요소로 Observable을 사용할 수 있습니다. 즉, 구독이 필요하지 않습니다. 이 방법에 대한 자세한 내용은 여기를 참조하십시오

--- 편집 3- '공식적인'솔루션 (2017/04/09)

나는 NGConf 에서이 질문에 대해 Ward Bell과 이야기했다. (나는 그에게 그가 옳았다 고 대답했다. ). 그는 또한 다음 공식 추천으로 내 SO 답변을 업데이트 할 수 있다고 말했습니다.

앞으로 우리 모두가 사용해야 할 해결책은 클래스 코드 내에서 호출하는 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

Angular tutorial의 Routing 장은 이제 다음과 같이 말합니다 : "라우터는 제공하는 옵저버 블을 관리하고 서브 스크립 션을 현지화합니다. 서브 스크립 션은 컴포넌트가 파괴 될 때 정리되어 메모리 누수를 방지하므로 구독을 취소 할 필요가 없습니다. 경로 매개 변수를 관찰 할 수 있습니다. " - 마크 라지 콕

여기의 논의 워드 벨이의 모든 것을 해명이 작품에 언급 경우 라우터 Observable 인에 대한 각 문서에 대한 Github의 문제에가.

--- 편집 1

소스 4

이러한면에서 NgEurope의 비디오 롭 Wormald 또한 라우터 Observables은 가입을 취소 할 필요가 없습니다 말했다. 그는 또한 언급 http서비스와 ActivatedRoute.params이에 2016년 11월에서 비디오를 .

--- 원래 답변

TLDR :

이 질문에 대해 (2) 종류가있다 Observables- 유한 값과 무한 값.

http Observables생산 한정된 (1)의 값과 같은 것을 DOM은 event listener Observables생산 무한 값.

subscribe비동기 파이프를 사용하지 않고 수동으로 호출하면 infiniteunsubscribe 에서 호출 합니다. Observables

유한 한 것들 에 대해 걱정하지 마십시오 RxJs.

소스 1

나는 각도의 Gitter에서 롭 Wormald에서 답변을 추적 여기 .

그는 말한다 (명확하고 강조하기 위해 재구성되었다)

그 경우 단일 값 시퀀스 (HTTP 요청 등)를 수동으로 정리 불필요 (수동 컨트롤러에 가입 가정)

" 완료된 시퀀스 인 경우 "(단일 값 시퀀스, la http 중 하나)

그 무한 순서 경우 , 당신은 취소해야 비동기 파이프가 당신을 위해 수행하는

또한 Observables에 대한 이 YouTube 비디오 에서 they clean up after themselves... Observables 의 맥락에서 complete(Promises와 같이 항상 1 개의 가치를 창출하고 끝나기 때문에 완료되는 약속과 같이) xhr이벤트 를 정리하기 위해 Promises에서 구독을 취소하는 것에 대해 걱정하지 않습니다. 청취자, 그렇지?).

소스 2

또한 Angular 2Rangle 안내서에서 읽습니다.

대부분의 경우 조기 취소를 원하거나 Observable이 구독보다 수명이 길지 않으면 구독 취소 메소드를 명시 적으로 호출 할 필요가 없습니다. Observable 연산자의 기본 동작은 .complete () 또는 .error () 메시지가 게시되는 즉시 구독을 처리하는 것입니다. RxJS는 대부분 "화재와 잊어 버리기"방식으로 사용되도록 설계되었습니다.

문구는 언제 our Observable has a longer lifespan than our subscription적용됩니까?

구성 요소 내부에서 구독이 작성 될 때 적용되며 Observable완료 되기 전에 (또는 오래 전에는) 소멸 됩니다.

http10 개의 값을 방출 하는 요청 또는 옵저버 블에 가입 하고 http요청이 반환 되기 전에 구성 요소가 파괴 되거나 10 개의 값이 방출 된 경우에도 이것을 의미로 읽습니다 .

요청이 리턴되거나 10 번째 값이 최종적으로 방출 Observable되면 완료되고 모든 자원이 정리됩니다.

소스 3

우리 가 동일한 Rangle 안내서 에서이 예제 를 보면 Subscriptionto route.params가 필요할 unsubscribe()때 알 수 없기 때문에 to 가 필요 하다는 것을 알 수 있습니다 params(새 값 방출).

경로 매개 변수가 계속 변경 될 가능성이있는 경우 (앱이 종료 될 때까지 기술적으로 변경 될 수 있음) 탐색하지 않으면 구성 요소가 손상 될 수 있으며 구독에 할당 된 리소스는 아직 없기 때문에 할당 completion됩니다.


15
전화 complete()자체로는 구독을 정리하지 않는 것 같습니다. 그러나 호출 next()한 다음에 complete()takeUntil()시퀀스가 끝날 때가 아니라 값이 생성 될 때만 중지 한다고 생각 합니다.
Firefly

2
@seangwright 빠른 유형의 멤버 테스트 Subject구성 요소 내부는 그것을 전환 ngIf트리거 ngOnInitngOnDestroy것이다 결코 완전하거나 배치받을 주제와 구독 (A 엮은 것으로, 쇼 finally구독에 - 연산자를). 나는 호출해야합니다 Subject.complete()에서 ngOnDestroy구독 스스로 뒤처리를 할 수 있습니다.
Lars

3
귀하의 --- 편집 3 은 매우 통찰력이 있습니다. 감사합니다! 후속 질문이 있습니다. takeUnitl접근법을 사용하는 경우 관찰 가능 항목을 수동으로 구독 취소하지 않아도됩니까? 그 경우입니까? 또한 왜 전화 next()를 해야 합니까? ngOnDestroy왜 전화하지 complete()않습니까?
uglycode

6
@seangwright 실망 스럽습니다. 추가 상용구가 성가시다.
spongessuck

3
편집 3 에서 사건의 맥락에서 논의 medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87
HankCa

89

구독을 많이하지 않아도되고 수동으로 구독을 취소 할 필요가 없습니다. 보스와 같은 구독을 처리 하려면 SubjecttakeUntil 콤보를 사용하십시오 .

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 이 제안한 대체 방법takeUntil 대신 takeWhile을 사용 합니다. 당신은 그것을 원할 수도 있지만, 이렇게하면 컴포넌트의 ngDestroy에서 Observable 실행이 취소되지 않습니다 (예를 들어, 시간이 많이 걸리는 계산을하거나 서버의 데이터를 기다릴 때). 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
그가 부울을 사용하여 상태를 유지한다면, "takeUntil"을 예상대로 작동시키는 방법은 무엇입니까?
Val

5
내가 사용하는 사이에 상당한 차이가 생각 takeUntil하고 takeWhile. 전자는 발사 될 때 즉시 소스에서 옵저버 블을 탈퇴하는 반면, 후자는 소스 옵저버 블에서 다음 값이 생성 되 자마자 탈퇴합니다. 소스 옵저버 블에 의해 값을 생성하는 것이 리소스 소비 작업 인 경우, 둘 중 하나를 선택하면 스타일 선호를 넘어 설 수 있습니다. 플 런크
Alex Che

1
@AlexChe 재미있는 plunk를 제공해 주셔서 감사합니다! 이것은 일반적인 경우가 아니라 takeUntilvs의 일반적인 사용법에 대한 매우 유효한 포인트입니다 takeWhile. 컴포넌트 소멸시 리스너를 구독 취소해야 할 때 () => alivein 과 같은 부울 값을 확인하기 만하면 takeWhile시간 / 메모리 소비 작업이 사용되지 않고 스타일링에 대한 차이점은 거의 없습니다 (이 경우 특정 경우).
메타 메이커

1
@metamaker 예를 들어, 컴포넌트에서을 구독하면 Observable내부적으로 일부 암호화 통화를 next채굴하고 모든 채굴 동전에 대한 이벤트를 발생시키고 해당 동전을 채굴하는 데 하루가 걸립니다. 으로 takeUntil우리가 소스 광산에서 탈퇴합니다 Observable즉시 한 번 ngOnDestroy우리의 구성 요소를 파괴하는 동안이라고합니다. 따라서 마이닝 Observable기능은이 과정에서 즉시 작동을 취소 할 수 있습니다.
Alex Che

1
우리가 사용하는 경우 OTOH, takeWhile의에 ngOnDestory우리는 단지 부울 변수를 설정합니다. 그러나 마이닝 Observable기능은 여전히 ​​최대 하루 동안 작동 할 수 있으며, next통화 중에 만 활성화 된 구독이 없으므로 취소해야한다는 것을 알게됩니다.
Alex Che

75

Subscription 클래스에는 흥미로운 기능이 있습니다.

Observable의 실행과 같은 일회용 자원을 나타냅니다. 서브 스크립 션에는 인수를 취하지 않고 서브 스크립 션이 보유한 자원을 폐기하는 중요한 방법 인 구독 취소가 있습니다.
또한 서브 스크립 션은 add () 메소드를 통해 그룹화되어 하위 서브 스크립 션을 현재 서브 스크립 션에 첨부합니다. 구독을 구독 취소하면 모든 자식 (및 손자)도 구독 취소됩니다.

모든 구독을 그룹화하는 집계 구독 오브젝트를 작성할 수 있습니다. 빈 구독을 작성하고 해당 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();
  }
}

이 접근법을 사용하고 있습니다. 허용 된 답변과 같이 takeUntil ()으로 접근 방식을 사용하는 것보다 이것이 더 좋은지 궁금합니다.
Manuel Di Iorio

내가 아는 결점은 없습니다. 나는 이것이 더 나은 것이라고 생각하지 않습니다.
Steven Liekens

2
공식적인 접근 방식과 구독을 수집하고 호출하는 방식 에 대한 자세한 내용은 medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87 을 참조하십시오 . (이 접근법은 나에게 훨씬 깔끔해 보인다.)takeUntilunsubscribe
Josh Kelley

3
이 답변의 하나의 작은 이익 : 당신이 있는지 확인하지 않아도 this.subscriptionsnull 인
user2023861가

1
같은 단지 추가 방법의 체인을 피할 수 sub = subsciption.add(..).add(..)많은 경우에 예상치 못한 결과가 발생하기 때문에 github.com/ReactiveX/rxjs/issues/2769#issuecomment-345636477
에브 게니 Generalov

32

Angular 구성 요소 내에서 옵저버 블 구독 취소에 대한 모범 사례 중 일부 :

의 인용 Routing & Navigation

컴포넌트에서 옵저버 블을 구독 할 때는 컴포넌트가 소멸 될 때 거의 항상 구독을 취소합니다.

이것이 필요하지 않은 예외적 인 관측 물이 몇 가지 있습니다. ActivatedRoute 옵저버 블은 예외입니다.

ActivatedRoute 및 해당 관찰 가능 항목은 라우터 자체와 절연되어 있습니다. 라우터는 더 이상 필요하지 않은 라우팅 된 구성 요소를 삭제하고 주입 된 ActivatedRoute가 함께 죽습니다.

어쨌든 구독을 취소하십시오. 무해하고 결코 나쁜 습관이 아닙니다.

그리고 다음 링크에 응답합니다.

Angular 구성 요소 내에서 관찰 가능한 구독 취소에 대한 모범 사례 중 일부를 수집하여 귀하와 공유했습니다.

  • http관찰 가능한 구독 취소는 조건부이므로 구성 요소가 사례별로 소멸 된 후에 실행되는 '구독 콜백'의 영향을 고려해야합니다. 우리는 각도가 http관찰 가능 객체를 탈퇴하고 청소한다는 것을 알고 있습니다 (1) , (2) . 이것은 자원의 관점에서 사실이지만, 이야기의 절반 만 말해줍니다. http구성 요소 내에서 직접 전화하는 것에 대해 이야기하고 http있으며 사용자가 구성 요소를 닫을 때 까지 응답 시간이 오래 걸렸습니다. 그만큼subscribe()구성 요소가 닫히고 파괴 된 경우에도 처리기는 계속 호출됩니다. 이로 인해 원치 않는 부작용이 생길 수 있으며 더 나쁜 시나리오에서는 응용 프로그램 상태가 손상됩니다. 콜백의 코드가 방금 폐기 된 것을 호출하려고하면 예외가 발생할 수 있습니다. 그러나 동시에 그들은 때때로 바람직하다. 예를 들어, 이메일 클라이언트를 작성 중이고 이메일 전송이 완료되면 소리가 울린다 고 가정 해 봅시다. 구성 요소가 닫혀 있어도 여전히 발생하기를 원할 것입니다 ( 8 ).
  • 완료되거나 오류가 발생한 옵저버 블의 구독을 취소 할 필요가 없습니다. 그러나 그렇게하는 데 아무런 해가 없습니다 (7) .
  • AsyncPipe구성 요소 삭제시 관찰 가능 항목을 자동으로 구독 해제하므로 가능한 한 많이 사용하십시오 .
  • 탈퇴에서 ActivatedRoute와 같은 관찰 가능한 route.params그들이 오랫동안 부모 / 호스트 구성 요소가 존재하는만큼을 가입 할 수있다 그들이 중첩 (구성 요소 선택과 TPL 내에서 추가) 또는 동적 구성 요소 내부에 가입하는 경우. 위의 인용문에서 언급 한 다른 시나리오에서는 문서에서 탈퇴 할 필요가 없습니다 Routing & Navigation.
  • Angular 서비스를 통해 노출되는 구성 요소간에 공유되는 전역 옵저버 블의 구독을 취소합니다 (예 : 구성 요소가 초기화되는 한 여러 번 구독 될 수 있음).
  • 이 서비스는 절대 파괴되지 않기 때문에 애플리케이션 범위 서비스의 내부 감시 대상을 구독 취소 할 필요가 없습니다. 전체 애플리케이션이 파괴되지 않는 한, 구독을 취소 할 실제 이유가 없으며 메모리 누수가 발생할 가능성이 없습니다. (6) .

    참고 : 범위가 지정된 서비스, 즉 구성 요소 공급자와 관련하여 구성 요소가 손상되면 해당 서비스가 삭제됩니다. 이 경우, 우리가이 제공자 내부의 Observable에 가입 ​​한 경우, OnDestroy문서에 따라 서비스가 파괴 될 때 호출되는 수명주기 후크를 사용하여 구독 취소를 고려해야 합니다.
  • 구독 취소로 인해 발생할 수있는 코드 혼잡을 피하려면 추상 기술을 사용하십시오. takeUntil (3)으로 구독을 관리 하거나 (4) Angular의 Observables 구독을 취소하는 가장 쉬운 방법에 언급 된 이 npm 패키지를 사용할 수 있습니다 .
  • 항상 구독을 취소 FormGroup같은 관찰 가능한 form.valueChangesform.statusChanges
  • 다음 Renderer2과 같은 서비스 옵저버 블을 항상 구독 취소하십시오.renderer2.listen
  • Angular Docs가 어떤 옵저버 블을 구독 해제 할 필요가 없는지 명시 적으로 알려줄 때까지 모든 옵저버 블 에서 메모리 누수 가드 단계로 구독을 취소하십시오 (문제 확인 : (5) RxJS Unsubscribing에 대한 문서 (Open) ).
  • 보너스 : HostListener필요한 경우 이벤트 리스너를 제거하는 데주의를 기울이고 이벤트 바인딩으로 인한 잠재적 인 메모리 누수를 방지 하기 위해 항상 각도 방법을 사용하여 각도와 같은 이벤트를 바인드하십시오.

좋은 최종 팁 : Observable이 자동으로 구독 취소 / 완료되는지 여부를 모르는 경우 complete콜백을 추가 subscribe(...)하고 구성 요소가 손상 될 때 호출되는지 확인하십시오.


6 번 답변은 옳지 않습니다. 서비스가 ngOnDestroy루트 수준 이외의 수준으로 제공 될 때 서비스가 삭제되고 나중에 제거되는 구성 요소에 명시 적으로 제공되는 서비스가 호출됩니다. 이 경우 서비스 내부 관찰 가능 항목을 구독 취소해야합니다
Drenai

@Drenai, 귀하의 의견에 감사하고 공손하게 동의하지 않습니다. 구성 요소가 파괴되면 구성 요소, 서비스 및 관찰 가능 항목은 모두 GC가되며 구성 요소와 멀리 떨어진 곳에 관찰 가능 항목에 대한 참조를 유지하지 않는 한이 경우 구독 취소는 쓸모가 없습니다. 구성 요소에 서비스 범위를
좁혔

파괴 된 서비스가 DI 계층에서 상위에있는 다른 서비스에 속하는 옵저버 블에 가입 한 경우 GC가 발생하지 않습니다. 의 구독을 취소하여이 시나리오를 피하십시오.이 ngOnDestroy서비스는 서비스가 파괴 될 때 항상 호출됩니다. github.com/angular/angular/commit/…
Drenai

1
@Drenai, 업데이트 된 답변을 확인하십시오.
Mouneer

2
@Tim 우선, Feel free to unsubscribe anyway. It is harmless and never a bad practice.귀하의 질문에 따라 다릅니다. 하위 구성 요소가 여러 번 시작된 경우 (예 : 내부에 추가 ngIf되거나 동적으로로드되는 경우) 동일한 옵저버에 여러 구독을 추가하지 않도록 구독을 취소해야합니다. 그렇지 않으면 필요하지 않습니다. 그러나 하위 구성 요소를 구독 취소하는 것이 더 재사용 가능하고 사용 방법과 분리되어 있기 때문에 구독을 선호합니다.
Mouneer

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()되어 GC가 청취자를 정리할 수있게 된 것 같습니다.

두 번째 예는의 구현에 따라 다릅니다 params. 오늘부터 앵귤러 공식 가이드는 더 이상 구독 취소를 표시하지 않습니다 params. 나는 src를 다시 살펴 보았고 그것이 params단지 BehaviorSubject 인 것을 알았습니다 . 이벤트 리스너 또는 타이머가 사용되지 않았고 전역 변수가 작성되지 않았으므로 생략해도 안전합니다 unsubscribe().

당신의 질문에 대한 결론 unsubscribe()은 관찰 가능 객체의 실행이 전역 변수를 생성하지 않거나 이벤트 리스너를 추가하거나 타이머를 설정하거나 메모리 누수를 유발하는 다른 일을하지 않는 것이 아니라면 항상 메모리 누수 방지를 위해 호출 하는 것입니다 .

확실하지 않은 경우, 관찰 가능한 구현을 조사하십시오. 옵저버 블 unsubscribe()이 일반적으로 생성자에 의해 반환되는 함수 인 정리 논리를로 작성했다면 호출을 진지하게 고려해야 할 충분한 이유가 unsubscribe()있습니다.


6

Angular 2 공식 문서는 구독을 취소 할 때와 안전하게 무시할 수있는시기에 대한 설명을 제공합니다. 이 링크를 살펴보십시오.

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

부모와 자녀가 서비스를 통해 통신 한 다음 파란색 상자 로 표시된 단락을 찾습니다 .

AstronautComponent가 파괴되면 구독을 캡처하고 구독을 취소합니다. 메모리 누수 가드 단계입니다. AstronautComponent의 수명은 앱 자체의 수명과 동일하므로이 앱에는 실제 위험이 없습니다. 더 복잡한 응용 프로그램에서는 항상 그렇지는 않습니다.

부모로서 MissionService의 수명을 제어하기 때문에이 Guard를 MissionControlComponent에 추가하지 않습니다.

이것이 도움이되기를 바랍니다.


3
구성 요소로 당신은 당신이 아이인지 아닌지 알 수 없습니다. 따라서 항상 모범 사례로 구독을 구독 취소해야합니다.
SeriousM

1
MissionControlComponent에 대한 요점은 실제로 부모인지 여부가 아니라 구성 요소 자체가 서비스를 제공한다는 것입니다. MissionControl이 손상되면 서비스 및 서비스 인스턴스에 대한 참조도 손상되므로 누출 가능성이 없습니다.
44 분

6

기반 : 클래스 상속을 사용하여 Angular 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()위에서 sean이 수락 한 솔루션과 같은 전화 가 없습니다 .
philn

4

공식 편집 # 3 답변 (및 변형)은 잘 작동하지만 나를 얻는 것은 관찰 가능한 구독에 관한 비즈니스 로직의 '진흙'입니다.

래퍼를 사용하는 또 다른 방법이 있습니다.

경고 : 실험 코드

subscribeAndGuard.ts 파일 은 줄 바꿈 .subscribe()하고 그 안에 줄 바꿈 할 새로운 Observable 확장명을 만드는 데 사용됩니다 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;
  }
}

다음은 두 개의 서브 스크립 션이있는 구성 요소입니다. 하나는 랩퍼가 있고 다른 하나는없는 것입니다. 유일한주의 사항은 OnDestroy를 구현 해야한다는 것입니다 (원하는 경우 빈 몸체 포함). 그렇지 않으면 Angular는 래핑 된 버전을 호출하는 것을 모릅니다.

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

데모 플런 커가 여기 있습니다

추가 참고 사항 : Re Edit 3- '공식'솔루션, 구독 전에 takeUntil () 대신 takeWhile ()을 사용하고 ngOnDestroy의 다른 Observable이 아닌 간단한 부울을 사용하여 단순화 할 수 있습니다.

@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에서 super ()를 호출해야한다는 것을 암시했습니다.

이 답변은 수퍼 콜을 피하고 "componentDestroyed $"를 기본 컴포넌트의 핵심으로 만드는 방법을 제공합니다.

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()자식 구성 요소에서 해당 메서드를 구현하는 경우 호출 해야합니다.

Ben Lesh : RxJS : Do n't Unsubscribe 의 추가 참조를 여기에서 찾으십시오 .


2

seangwright의 솔루션을 사용해 보았습니다 (편집 3)

타이머 또는 간격으로 생성 된 Observable에서는 작동하지 않습니다.

그러나 다른 접근법을 사용하여 작동하게했습니다.

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'를 바인딩하려면 화살표 기능을 사용해야합니다.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로 업그레이드하면 죽일 것 같습니다. 왜 그런지 아는 사람이 있습니까?
ymerej

1

일반적으로 구성 요소가 손상되면 구독을 취소해야하지만 Angular는 예를 들어 Angular4의 새 부 버전에서 구독을 라우팅하기 위해이 섹션이 있습니다.

구독을 취소해야합니까?

라우팅 및 탐색 페이지의 ActivatedRoute : 경로 정보를위한 원 스톱 상점 섹션에 설명 된대로 라우터는 라우터가 제공하는 옵저버 블을 관리하고 구독을 지역화합니다. 구성 요소가 손상되면 구독이 정리되어 메모리 누수를 방지하므로 경로 paramMap Observable에서 구독을 취소 할 필요가 없습니다.

또한 아래 예제는 Angular의 구성 요소를 만들어서 파괴하는 좋은 예입니다. onInit가 필요한 경우 구성 요소가 OnDestroy를 구현하는 방법을 살펴보십시오. 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
혼란스러워. 여기서 뭐라고합니까? 당신 (Angular 최근 문서 / 메모)은 Angular가 그것을 처리 한 다음 나중에 구독 취소가 좋은 패턴임을 확인하는 것으로 보입니다. 감사.
jamie

1

위에서 언급 한 상황에 대한 또 다른 짧은 추가 사항은 다음과 같습니다.

  • 구독 된 스트림의 새 값이 더 이상 필요하지 않거나 중요하지 않은 경우 항상 구독을 취소하면 트리거 수가 줄어들고 경우에 따라 성능이 향상됩니다. 구독 된 데이터 / 이벤트가 더 이상 존재하지 않거나 모든 새로운 스트림에 대한 새 구독이 필요한 (예 : 새로 고침 등) 구성 요소와 같은 경우 구독 취소에 대한 좋은 예입니다.

0

ngOnDestroy 함수 (angular lifeCycle)의 SPA 응용 프로그램에서 구독 할 때마다 구독취소 해야합니다. 이점 => 상태가 너무 무거워지는 것을 방지합니다.

예를 들면 다음과 같습니다. component1 :

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

component2에서 :

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

구독을 처리하기 위해 "구독자"클래스를 사용합니다.

구독자 클래스는 다음과 같습니다.

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클래스를 사용 하여 지저분한 코드가 아닌 Observable을 구독 취소 할 수 있습니다 .

우리는이 작업을 수행 할 수 normal variable있지만이 될 것입니다 override the last subscription그래서를 방지 서브 스크라이브 모든 새에,이 방법은 당신이 Obseravables 더 많은 수를 처리 할 때 매우 유용하며, 같은 Obeservables의 입력 BehavoiurSubjectSubject

신청

Observable의 실행과 같은 일회용 자원을 나타냅니다. 서브 스크립 션에는 인수를 취하지 않고 서브 스크립 션이 보유한 자원을 폐기하는 중요한 방법 인 구독 취소가 있습니다.

두 가지 방법으로 사용할 수 있습니다.

  • 서브 스크립 션을 서브 스크립 션 어레이로 직접 푸시 할 수 있습니다

     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

구독 취소를위한 쉽고 일관된 솔루션 인 SubSink 패키지

: 아무도 그것을 언급하지 않았다, 나는 워드 벨에 의해 생성 된 Subsink 패키지 추천하고 싶은 https://github.com/wardbell/subsink#readme을 .

우리가 모두 그것을 사용하는 여러 개발자 였기 때문에 프로젝트에서 사용하고 있습니다. 모든 상황에서 일관된 방식으로 일할 수 있도록 도와줍니다.


0

AsyncSubject를 들어 http 요청의 관찰 가능 항목 과 같은 결과를 내 보낸 직후 완료되는 관찰 가능 항목의 경우 구독을 취소 할 필요가 없습니다. unsubscribe()그것들 을 요구 하는 것은 아프지 않지만 관찰 가능할 경우 closed구독 취소 방법 은 아무것도하지 않습니다 .

if (this.closed) {
  return;
}

시간이 지남에 따라 여러 값을 방출하는 오래 지속되는 관측 가능 항목 (예 : a BehaviorSubject또는 a 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하나의 값만 방출하는 Observable에 파이프를 추가 해도 손상되지 않습니다.

개발 중에 소스 관찰 가능 이벤트가 여러 이벤트를 생성하면 실패 하는 single파이프 를 사용할 수 있습니다. 이를 통해 관찰 가능 유형과 구독 취소가 필요한지 여부를 탐색 할 수 있습니다.

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

first하고는 single모두 파이프 옵션 술어를 취할 수 있습니다 매우 유사 보이지만 차이는 중요하고 잘 요약되어 여기에 유래 답변 :

먼저

첫 번째 항목이 표시되는 즉시 방출됩니다. 그 직후에 완료됩니다.

단일

소스 관찰 가능 이벤트가 여러 이벤트를 생성하면 실패합니다.


참고 공식 문서를 참조하여 가능한 한 정확하고 완전하게 답변하려고 노력했지만 중요한 것이 누락 된 경우 의견을 보내주십시오 ...


-1

--- Angular 9 및 Rxjs 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경우 구성 요소가 초기화 될 때 한 번만 발생합니다.
class SampleComponent implements OnInit {

  private sampleObservable$: Observable<any>;

  constructor () {}

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

async파이프 도 있습니다 . 그러나이 템플릿에서는 Angular 구성 요소가 아닌 템플릿에서 사용됩니다.


첫 번째 예는 불완전합니다.
Paul-Sebastian Manole
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.