메모리 누수 방지
약 5년 전에 벤 레쉬는 아주 좋은 글을 썼는데 제목은 RxJS: Don’t Unsubscribe이었다.물론 저자는 우리에게 자신의 삶에 영원히 관심을 갖지 말라고 말하지 않았다
Subscription
.그의 말은 우리가 모든 과정에서 수동으로 집행할 필요가 없다는 것을 찾아야 한다는 것이다. .unsubscribe()
우리의 임무를 시작합시다!우리의 노선도
일부 전역 구성 요소(예를 들어 AppComponent)의 생존 기간은 응용 프로그램 자체의 생존 기간과 같다.만약 우리가 이러한 상황을 처리하고 있다는 것을 알았다면
.subscribe()
메모리 유출 보호 절차를 제공하지 않은 상황에서 관찰할 수 있는 대상을 관찰할 수 있다.그러나 모든 개발자에게 각도 응용 프로그램의 실현 과정에서 메모리 유출을 처리하는 것은 관건적인 임무이다.우리는 메모리 유출의 의미를 보여주기 위해 탐색을 시작할 것이다. 우리는 먼저 .unsubscribe()
의 '전통적인' 방식으로 이 문제를 해결하고 우리가 더 좋아하는 모델을 탐색할 것이다.엉터리 공개 구독
두 개의 라우팅 구성 요소
FirstComponent
와 SecondComponent
(첫 번째 Cmp와 두 번째 Cmp 내비게이션 링크 단추) 를 포함하는 간단한 프레젠테이션 프로그램이 있습니다.FirstComponent
(경로/first
에 대응하는) 구독 관찰 가능timer1$
, ScreenMessagesComponent
를 통해MessageService
에 메시지를 발송한다.정보가 화면 하단에 표시됩니다.Live Example
export class FirstComponent implements OnInit {
timer1$ = timer(0, 1000);
constructor(private messageService: MessageService) {}
ngOnInit(): void {
this.timer1$.subscribe((val) =>
this.messageService.add(`FirstComponent timer1$: ${val}`)
);
}
}
우리가 /second
경로를 내비게이션했을 때 FirstComponent
는 이미 파괴되었다.그러나 우리는 상술한 구독에서 전해진 소식을 여전히 볼 수 있다.이것은 우리가 '닫힌 문' 을 잊어버렸기 때문이다. 우리의 응용 프로그램에는 오픈Subscription
이 하나 있다.우리가 왔다 갔다 하면서 점점 더 많은 구독을 추가함에 따라, 이 구독들은 앱이 닫힐 때만 닫힌다.우리는 메모리 유출을 처리해야 한다.구형 구독 해지
상술한 문제를 해결하는 간단한 방법은 실현lifecycle hook방법
ngOnDestroy()
이다.공식 문서에서 보듯이...Unsubscribe Observables and detach event handlers to avoid memory leaks...
export class FirstComponent implements OnInit, OnDestroy {
private timer1$ = timer(0, 1000);
private subscription: Subscription;
constructor(private messageService: MessageService) {}
ngOnInit(): void {
this.subscription = this.timer1$.subscribe((val) =>
this.messageService.add(`FirstComponent timer1$: ${val}`)
);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
그 밖에 우리가 여러 개
Subscription
가 있다면 우리는 모든 사람을 위해 똑같은 일을 해야 한다.export class FirstComponent implements OnInit, OnDestroy {
private timer1$ = timer(0, 1000);
private timer2$ = timer(0, 2500);
private subscription1: Subscription;
private subscription2: Subscription;
constructor(private messageService: MessageService) {}
ngOnInit(): void {
this.subscription1 = this.timer1$.subscribe((val) =>
this.messageService.add(`FirstComponent timer1$: ${val}`)
);
this.subscription2 = this.timer2$.subscribe((val) =>
this.messageService.add(`FirstComponent timer2$: ${val}`)
);
}
ngOnDestroy(): void {
this.subscription1.unsubscribe();
this.subscription2.unsubscribe();
}
}
만약 우리가 구독이 하나 또는 두 개 없고 .unsubscribe()
호출 수를 줄이려고 한다면, 우리는 아버지 Subscription
를 만들고 아들 Subscription
을 추가할 수 있습니다.부모 구독을 취소하면 그 안에 추가된 모든 하위 구독도 구독을 취소합니다.Live Example
export class FirstComponent implements OnInit, OnDestroy {
private timer1$ = timer(0, 1000);
private timer2$ = timer(0, 2500);
private subscription = new Subscription();
constructor(private messageService: MessageService) {}
ngOnInit(): void {
this.subscription.add(
this.timer1$.subscribe((val) =>
this.messageService.add(`FirstComponent timer1$: ${val}`)
)
);
this.subscription.add(
this.timer2$.subscribe((val) =>
this.messageService.add(`FirstComponent timer2$: ${val}`)
)
);
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
부모.unsubscribe()
를 사용하면 우리는 많은 속성에 관심을 가질 필요가 없다. 우리도 하나만 실행한다AsyncPipe
.비동기 파이프
AsyncPipe 대박!구성 요소의 템플릿에 '반응식' 으로 데이터를 표시하려고 할 때, 그 데이터는 적수가 없습니다.
The async pipe subscribes to an Observable or Promise and returns the latest value it has emitted. When a new value is emitted, the async pipe marks the component to be checked for changes. When the component gets destroyed, the async pipe unsubscribes automatically to avoid potential memory leaks.
Live Example
@Component({
selector: 'app-first',
template: `
<p>first component works!</p>
<p>{{ timer3$ | async }}</p>
`,
})
export class FirstComponent implements OnInit, OnDestroy {
...
timer3$ = timer(0, 1000);
...
}
.subscribe()
를 사용하려면 수동.unsubscribe()
또는 takeUntil
이 필요하지 않습니다.RxJS 연산자
RxJS는 관찰 시퀀스로 비동기적이고 이벤트 기반의 프로그램을 작성하는 라이브러리입니다.그것은 다음과 같은 훌륭한 운영사들이 있다.
Lets values pass until a second Observable, notifier, emits a value. Then, it completes.
우선, 나는 본문에서 묘사한 위험을 언급하고 싶다. RxJS: Avoiding takeUntil Leaks.
pipe
조작원은 반드시 destroy$
중의 마지막 조작원이어야 한다.If the
takeUntil
operator is placed before an operator that involves a subscription to another observable source, the subscription to that source might not be unsubscribed whentakeUntil
receives its notification.
Live Example
export class FirstComponent implements OnInit, OnDestroy {
...
private destroy$ = new Subject<void>();
constructor(private messageService: MessageService) {}
ngOnInit(): void {
this.timer1$
.pipe(takeUntil(this.destroy$))
.subscribe(
(val) => this.messageService.add(`FirstComponent timer1$: ${val}`),
(err) => console.error(err),
() => this.messageService.add(`>>> FirstComponent timer1$ completed`)
);
this.timer2$
.pipe(takeUntil(this.destroy$))
.subscribe(
(val) => this.messageService.add(`FirstComponent timer2$: ${val}`),
(err) => console.error(err),
() => this.messageService.add(`>>> FirstComponent timer2$ completed`)
);
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}
여기Observable
는 우리의 두 번째ngOnDestroy()
(통지 프로그램)로 내부complete()
의 생명주기 갈고리를 보내고 이런 방식으로 데이터 흐름의 완성을 촉발한다.이런 방법의 장점 중 하나는 실제로 관찰할 수 있는 것을 완성했기 때문에 .unsubscribe()
리셋을 호출하는 것이다.우리가 전화ngOnDestroy()
를 했을 때, 우리는 예약 취소 통지를 받을 수 없습니다.결점
상기 모든 해결 방안은 실제적으로 우리의 문제를 해결했지만 적어도 하나의 단점이 있다. 우리는 모든 구성 요소에서 자신의 일을 반복해서 실현해야 한다
takeUntil
.샘플 파일을 더 줄일 수 있는 더 좋은 방법이 있습니까?네, 저희는 ngOnDestroy()
와 Angular's DI mechanism를 이용할 것입니다.서비스 제거
Live Example
우선
FirstComponent
한 서비스로 이동합니다.import { Injectable, OnDestroy } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class DestroyService extends Subject<void> implements OnDestroy {
ngOnDestroy() {
this.next();
this.complete();
}
}
DestroyService
서비스 인스턴스(공급자 메타데이터 배열을 통해)를 제공하고 그 구조 함수를 통해 해당 인스턴스를 자신에게 주입합니다.@Component({
selector: 'app-first',
template: `<p>first component works!</p>`,
providers: [DestroyService],
})
export class FirstComponent implements OnInit {
...
constructor(
private messageService: MessageService,
private readonly destroy$: DestroyService
) {}
ngOnInit(): void {
...
}
}
우리는 이전과 완전히 같은 결과를 얻었다.우리는 필요한 모든 구성 요소에서 takeUntil
의 실례를 제공할 수 있다.결론
최종적으로 RxJS 구독을 관리하는 더 좋은 방법은 각도 서비스
.unsubscribe()
를 통해 사용하는 것이라고 생각합니다.몇 가지 이점은 다음과 같습니다..next()
또는 .complete()
, ngOnDestroy()
방법의 기회 감소GitHub repo는 예제here를 제공합니다.
Reference
이 문제에 관하여(메모리 누수 방지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/theoklitosbam7/avoid-memory-leaks-in-angular-5gla텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)