Http 메소드로 생성 된 Observable을 구독 취소해야합니까?


211

메모리 누수를 방지하기 위해 Angular 2 http 호출을 구독 취소해야합니까?

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


답변:


253

대답은 '아니요'입니다. Ng2그것을 스스로 청소합니다.

Angular의 Http XHR 백엔드 소스의 Http 서비스 소스 :

여기에 이미지 설명을 입력하십시오

complete()결과를 얻은 후 실행 방법을 확인하십시오 . 이는 실제로 완료시 구독을 취소 함을 의미합니다. 따라서 직접 할 필요는 없습니다.

다음은 유효성을 검사하는 테스트입니다.

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

예상 한대로 결과를 얻고 관찰 가능한 연산자로 완료 한 후에는 항상 자동으로 구독이 취소 된 것을 알 수 있습니다. 이는 타임 아웃 (# 3)에 발생하므로 모든 작업이 완료되면 관찰 가능 상태를 확인할 수 있습니다.

그리고 결과

여기에 이미지 설명을 입력하십시오

따라서 Ng2자동 수신 거부 로 누출이 발생하지 않습니다 !

언급하기 좋음 : 이것은 Observable로 분류 finite되는데, infinite Observable이는 click예를 들어 DOM 리스너 처럼 무한한 데이터 스트림이 방출 될 수 있습니다 .

감사합니다. @rubyboy


17
응답이 시작되기 전에 사용자가 페이지를 떠나거나 사용자가보기를 떠나기 전에 응답이 나오지 않으면 어떻게됩니까? 누출이 발생하지 않습니까?
Thibs

1
위의 내용도 알고 싶습니다.
1984 년

4
@ 1984 정답은 항상 구독 취소입니다. 이 답변은이 의견에서 제기 된 이유 때문에 완전히 잘못되었습니다. 탐색하는 사용자는 메모리 링크입니다. 또한 구독하는 코드가 사용자가 이동 한 후에 실행되면 오류가 발생하거나 예기치 않은 부작용이 발생할 수 있습니다. 모든 Observable에서 takeWhile (() => this.componentActive)를 사용하고 ngOnDestroy에서 this.componentActive = false를 설정하여 구성 요소의 모든 Observable을 정리하는 경향이 있습니다.
레니

4
여기에 표시된 예는 깊은 각도의 개인 API를 다룹니다. 다른 개인 API를 사용하면 예고없이 업그레이드하여 변경할 수 있습니다. 경험의 규칙 : 공식적으로 문서화되지 않은 경우, 어떤 가정도하지 마십시오. 예를 들어, AsynPipe에는 자동으로 구독 / 구독 취소 할 수있는 명확한 문서가 있습니다. HttpClient 문서는 그것에 대해 언급하지 않습니다.
YoussefTaghlabi

1
@YoussefTaghlabi, FYI, 공식 앵귤러 문서 ( angular.io/guide/http )는 "AsyncPipe가 자동으로 구독 (및 구독 취소)"한다고 언급합니다. 따라서 실제로 공식적으로 문서화되었습니다.
Hudgi

96

사람들은 무엇에 대해 이야기하고 있습니까 !!!

확인 가능하므로 구독을 취소해야하는 두 가지 이유가 있습니다. 중요한 두 번째 이유에 대해 아무도 많이 이야기하지 않는 것 같습니다!

1) 자원을 정리하십시오. 다른 사람들이 말했듯이 이것은 HTTP 옵저버 블에게는 무시할만한 문제입니다. 그냥 정리해

2) subscribe핸들러가 실행되지 않도록하십시오 .

(HTTP의 경우 실제로 브라우저에서 요청을 취소하므로 응답을 읽는 데 시간을 낭비하지 않지만 실제로는 아래 주요 요점을 제외하고는 아닙니다.)

숫자 2의 관련성은 구독 처리기의 기능에 따라 다릅니다.

귀하의 경우 subscribe()핸들러 함수가 호출 어떤이 닫혀있는 경우 바람직하지 않은 또는 배치되어 부작용의 종류를 가지고 당신은 탈퇴 (또는 조건부 논리를 추가) 실행되는 것을 방지하기 위해해야합니다.

몇 가지 경우를 고려하십시오.

1) 로그인 양식. 사용자 이름과 비밀번호를 입력하고 '로그인'을 클릭하십시오. 서버 속도가 느리고 Esc를 눌러 대화 상자를 닫으려면 어떻게해야합니까? 로그인하지 않았다고 가정 할 수도 있지만 탈출을 누른 후에 http 요청이 반환되면 거기에있는 모든 논리를 계속 실행합니다. 이로 인해 계정 페이지로 리디렉션되거나 원치 않는 로그인 쿠키 또는 토큰 변수가 설정 될 수 있습니다. 이것은 아마도 사용자가 기대 한 것이 아닐 것입니다.

2) '이메일 보내기'양식.

는 IF subscribe'sendEmail'에 대한 핸들러가 트리거 유사한 무언가를 당신이 예외 또는 원치 않는 동작을 얻을 수 있습니다 배치 된 액세스 아무것도 다른 페이지 또는 시도로 전송, 애니메이션 '이메일이 전송됩니다'.

또한 unsubscribe()'취소'를 의미 하지 않도록주의하십시오 . HTTP 메시지가 비행 unsubscribe()중이면 HTTP 요청이 이미 서버에 도달 한 경우 HTTP 요청을 취소하지 않습니다. 답장 만 취소됩니다. 그리고 아마도 이메일이 전송 될 것입니다.

UI 구성 요소 내에서 직접 전자 메일을 보내도록 구독을 만들면 처리를 취소 할 수 있지만 UI가 아닌 중앙 집중식 서비스에서 전자 메일을 보내는 경우에는 그럴 필요가 없습니다.

3) 파괴 / 폐쇄 된 각도 성분. 에 아직 실행중인 http 옵저버 블은에서 구독을 취소하지 않으면 논리를 완료하고 실행합니다 onDestroy(). 결과가 사소한 지 여부는 구독 처리기에서 수행하는 작업에 따라 다릅니다. 더 이상 존재하지 않는 것을 업데이트하려고하면 오류가 발생할 수 있습니다.

구성 요소가 폐기 된 경우 원하는 작업이있을 수도 있고 그렇지 않은 경우도 있습니다. 예를 들어 보낸 이메일에 대해 'swoosh'소리가납니다. 구성 요소가 닫혀 있어도이 기능을 원할 수도 있지만 구성 요소에서 애니메이션을 실행하려고하면 실패합니다. 이 경우 subscribe 내부의 일부 조건부 논리가 해결책이 될 것입니다. http 관찰 가능을 구독 취소하지 않으려 고합니다.

따라서 실제 질문에 대한 답으로 메모리 누수를 피하기 위해 할 필요가 없습니다. 그러나 예외를 발생 시키거나 응용 프로그램 상태를 손상시킬 수있는 코드를 실행하여 원하지 않는 부작용이 발생하지 않도록하려면 종종 수행해야합니다.

팁 : 고급 부울 경우 유용한 부울 속성 이 Subscription포함되어 있습니다 closed. HTTP의 경우 완료시 설정됩니다. Angular에서는 일부 상황에서 핸들러 가 확인할 수 있는 _isDestroyed속성 을 설정하는 것이 유용 ngDestroy할 수 있습니다 subscribe.

팁 2 : 여러 구독을 처리하는 경우 임시 new Subscription()개체와 add(...)다른 구독을 만들 수 있습니다. 따라서 기본 구독을 구독 취소하면 추가 된 모든 구독도 구독 취소됩니다.


2
또한 원시 http 오버 뷰어 블을 반환하는 서비스가 있고 구독하기 전에이를 파이프하면 기본 http 옵저버 블이 아닌 최종 옵저버 블 만 구독 취소하면됩니다. 사실 당신은 http에 직접 가입하지 않아도되므로 할 수 없습니다.
Simon_Weaver

구독을 취소하면 브라우저 요청이 취소 되더라도 서버는 여전히 요청에 대해 작동합니다. 작업을 중단하기 위해 서버를 친밀하게 만드는 방법이 있습니까? http 요청을 빠르게 연속해서 보내고 있는데,이 중 대부분은 구독 취소로 취소됩니다. 그러나 서버는 여전히 클라이언트가 취소 한 요청에 대해 작동하므로 합법적 인 요청이 대기합니다.
bala

@bala RxJS와는 아무런 관련이없는 자체 메커니즘을 생각해보아야합니다. 예를 들어 요청을 테이블에 넣고 백그라운드에서 5 초 후에 실행할 수 있습니다. 취소해야 할 것이 있으면 이전 행에서 플래그를 삭제하거나 설정하여 실행을 중지합니다. 전적으로 귀하의 응용 프로그램에 달려 있습니다. 그러나 서버가 차단 중이라고 언급 했으므로 한 번에 하나의 요청 만 허용하도록 구성 될 수 있지만 사용중인 항목에 따라 달라집니다.
Simon_Weaver

🔥🔥🔥Tip 2-하나의 함수 만 호출하여 여러 구독을 구독 취소하려는 모든 사람을위한 PRO-TIP, 🔥🔥🔥입니다. ngDestroy에서 .add () 메소드를 사용하고 .unsubscribe ()보다 추가하십시오.
abhay tripathi

23

호출 unsubscribe방법은이 방법은 호출 이후 진행중인 HTTP 요청을 취소하는 대신이다 abort부하 및 오류 이벤트에 대한 기본 XHR 객체 및 제거 청취자 하나를 :

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

즉, unsubscribe청취자를 제거합니다 ... 그래서 좋은 아이디어 일 수는 있지만 단일 요청에 필요하다고 생각하지 않습니다. ;-)

티에리가 당신을 도울 수 있기를 바랍니다.


: | 터무니없는 : | 나는 멈추는 방법을 찾고 있었지만 ... 어쩌면 궁금했고, 코딩하는 경우 일부 시나리오에서 y = x.subscribe((x)=>data = x); 다음 과 같이 할 것입니다 : 입력에서 사용자가 변경되고 x.subscribe((x)=>cachlist[n] = x); y.unsubscribe()서버 리소스를 사용했습니다. 당신을 y.stop()버리지
마라

16

수신 거부는 A는 MUST 당신이 원하는 경우 결정 모든 네트워크 속도에서 동작을.

컴포넌트 A가 탭에 렌더링되었다고 가정 하십시오. 버튼을 클릭하여 'GET'요청을 보냅니다. 응답이 되돌아 오려면 200ms가 걸립니다. 따라서 기계가 사용자보다 빠르며 탭이 닫히고 구성 요소 A가 파괴되기 전에 http 응답이 처리되어 완료된다는 것을 알고 언제든지 탭을 닫는 것이 안전합니다.

매우 느린 네트워크에서는 어떻습니까? 버튼을 클릭하면 'GET'요청이 응답을받는 데 10 초가 걸리지 만 탭을 닫기로 결정하기까지 5 초가 걸립니다. 그러면 나중에 가비지 수집되는 구성 요소 A가 손상됩니다. 잠깐만! 구독을 취소하지 않았습니다. 이제 5 초 후에 응답이 다시 표시되고 삭제 된 구성 요소의 로직이 실행됩니다. 이 실행은 현재 고려 out-of-context되어 매우 낮은 성능을 포함하여 많은 것을 초래할 수 있습니다.

따라서 takeUntil()구성 요소가 손상 될 때 http 호출 을 사용 하고 구독을 취소 하는 것이 가장 좋습니다 .

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

그것은 사용하기 괜찮 BehaviorSubject()대신 그냥 전화 complete()에서 ngOnDestroy()?
Saksham

여전히 구독을 취소해야합니다. 왜 BehaviourSubject다른가요?
벤 탈리아도 로스

HttpClient 옵저버 블은 유한 옵저버 블이므로 구독을 취소 할 필요가 없습니다. 즉, 값을 방출 한 후에 완료됩니다.
Yatharth Varshney

어쨌든 구독을 취소해야합니다. 오류를 가로채는 인터셉터가 있다고 가정하지만 이미 오류를 일으킨 페이지를 남겼습니다. 다른 페이지에 오류 메시지가 표시됩니다. 또한 상태 점검 페이지도 생각해보십시오. 모든 microservices를 호출 ... 등등
워싱턴 오 Guedes

12

또한 새로운 HttpClient 모듈에서도 동일한 동작이 유지됩니다. 꾸러미 /common/http/src/jsonp.ts


위의 코드는 단위 테스트에서 나온 것으로 보이며, HttpClient를 사용하면 .complete ()를 직접 호출해야합니다 ( github.com/angular/angular/blob/… )
CleverPatrick

예, 맞습니다. 그러나 ( github.com/angular/angular/blob/… ) 검사 하면 동일하게 표시됩니다. 주요 질문과 관련하여 명시 적으로 구독을 취소해야합니까? Angular 자체에서 처리되기 때문에 대부분의 경우는 없습니다. 긴 응답이 발생하고 다른 경로로 이동하거나 구성 요소가 손상되었을 수 있습니다.이 경우 더 이상 예외가 발생하지 않는 항목에 액세스하려고 할 수 있습니다.
Ricardo Martínez

8

자동으로 완료되는 관찰 가능 항목 (예 : Http, 호출)을 구독 취소해서는 안됩니다. 그러나와 같은 무한 관측 가능 항목을 구독 취소해야합니다 Observable.timer().


6

테스트 후 잠시 동안 HttpClient의 문서와 소스 코드를 읽으십시오.

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/

앵귤러 대학교 : https://blog.angular-university.io/angular-http/

이 특정 유형의 Observable은 단일 값 스트림입니다. HTTP 요청이 성공하면이 Observable은 하나의 값만 방출 한 다음 완료됩니다.

그리고 "do i NEED"의 전체 이슈에 대한 답변은 구독을 취소합니까?

때에 따라 다르지. HTTP 호출 메모리 누출은 문제가되지 않습니다. 문제는 콜백 함수의 논리입니다.

예 : 라우팅 또는 로그인.

전화가 로그인 전화 인 경우 "구독 취소"할 필요는 없지만 사용자가 페이지를 떠날 경우 사용자가없는 경우 응답을 올바르게 처리해야합니다.


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

성가신 것에서 위험한 것까지

이제 네트워크가 평소보다 느리고 전화가 5 초 이상 걸리고 사용자가 로그인보기를 종료하고 "지원보기"로갑니다.

구성 요소가 활성 상태가 아닌 구독 일 수 있습니다. 응답이있는 경우, handleResponse () 구현에 따라 사용자가 갑자기 다시 라우팅됩니다.

이것은 아닙니다 좋지 .

또한 사용자가 아직 로그인하지 않았다고 믿고 PC를 떠났다고 상상해보십시오. 그러나 논리는 사용자를 로그인하여 보안 문제가 있습니다.

탈퇴하지 않고 무엇을 할 수 있습니까?

보기의 현재 상태에 따라 전화를 겁니다.

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

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

.pipe(takeWhile(value => this.isActive))보기가 활성화 된 경우에만 응답이 처리되는지 확인하는 사용자 입니다.


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

그러나 구독이 메모리 누수를 일으키지 않는지 어떻게 알 수 있습니까?

"teardownLogic"이 적용되면 로그 할 수 있습니다.

서브 크립 션이 비어 있거나 구독 취소되면 서브 스크립 션의 teardownLogic이 호출됩니다.


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

구독을 취소 할 필요가 없습니다. 논리에 문제가 있으면 구독에 문제를 일으킬 수 있는지 알아야합니다. 그리고 그들을 돌봐. 대부분의 경우 문제는 아니지만 자동화와 같은 중요한 작업에서는 특히 "구독 취소"또는 파이프 또는 조건부 콜백 함수와 같은 다른 논리를 사용하여 예기치 않은 동작을 처리해야합니다.

왜 항상 구독을 취소하지 않습니까?

풋 또는 포스트 요청을한다고 상상해보십시오. 서버는 메시지를 수신하지만 응답에는 시간이 걸립니다. 구독을 취소하면 게시물을 취소하거나 게시하지 않습니다. 그러나 구독을 취소하면 대화 나 토스트 / 메시지 등을 통해 응답을 처리하거나 사용자에게 알릴 기회가 없습니다.

Wich는 사용자가 풋 / 포스트 요청이 완료되지 않았다고 믿게합니다.

따라서 다릅니다. 이러한 문제를 처리하는 방법은 디자인 결정입니다.


4

반드시 읽어야합니다 기사를 . http에서도 항상 구독을 취소 해야하는 이유를 보여줍니다 .

요청을 작성한 후 백엔드로부터 응답을 받기 전에 구성 요소가 불필요하다고 간주하여이를 폐기하면 구독이 구성 요소에 대한 참조를 유지하여 메모리 누수를 일으킬 가능성이 있습니다.

최신 정보

위의 확인은 사실 인 것처럼 보이지만 어쨌든 대답이 다시 오면 http 구독이 어쨌든 파괴됩니다.


1
예, 실제로 브라우저의 네트워크 탭에 빨간색 '취소'가 표시되어 작동하는지 확인할 수 있습니다.
Simon_Weaver

-2

RxJS observable은 기본적으로 연결되어 있으며 구독에 따라 작동합니다. Observable과 움직임을 만들면 Observable이 자동으로 닫히고 구독이 취소됩니다.

그들은 같은 방식으로 감시자들과 작동하지만 순서는 다릅니다. 구성 요소가 파괴 될 때 구독을 취소하는 것이 더 좋습니다 . this. $ manageSubscription.unsubscibe ()

아래와 같은 문법을 관찰 할 수 있다면

** 새로운 Observable 반환 ((observer) => {** // 차가운 상태에서 관찰 가능 ** observer.complete () **}) **

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