你需要取消订阅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());
...
取消订阅是必须的,如果你想在所有的网络速度上有一个确定的行为。
想象一下组件A是在一个选项卡中呈现的——你点击一个按钮发送一个“GET”请求。响应返回需要200毫秒。所以,你在任何时候都可以安全地关闭选项卡,因为机器会比你更快&在选项卡关闭和组件A被销毁之前处理并完成http响应。
How about on a very slow network? You click a button, the 'GET' request takes 10 seconds to receive its response, but 5 seconds into waiting you decide to close the tab. That will destroy component A to be garbage-collected at a later time. Wait a minute!, we did not unsubscribe -- now 5 seconds later, a response comes back and the logic in the destroyed component will be executed. That execution is now considered out-of-context and can result in many things including very low performance and data/state corruption.
因此,最佳实践是使用takeUntil(),并在组件销毁时从http调用订阅中取消订阅。
注意:
RxJS不是Angular特有的
模板中使用的异步管道会在销毁时自动取消订阅
取消订阅多次没有负面影响,除了额外的无操作呼叫
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
}
}
所以答案是不,你不需要。Ng2会自行清理。
Http服务源,来自Angular的Http XHR后端源:
注意它在得到结果后是如何运行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自动退订时不会有泄漏!
值得一提的是:这个可观察对象被归类为有限的,与无限的可观察对象相反,无限的可观察对象是一个无限的数据流,可以像DOM点击侦听器一样发出。
谢谢@rubyboy的帮助。
取消订阅是必须的,如果你想在所有的网络速度上有一个确定的行为。
想象一下组件A是在一个选项卡中呈现的——你点击一个按钮发送一个“GET”请求。响应返回需要200毫秒。所以,你在任何时候都可以安全地关闭选项卡,因为机器会比你更快&在选项卡关闭和组件A被销毁之前处理并完成http响应。
How about on a very slow network? You click a button, the 'GET' request takes 10 seconds to receive its response, but 5 seconds into waiting you decide to close the tab. That will destroy component A to be garbage-collected at a later time. Wait a minute!, we did not unsubscribe -- now 5 seconds later, a response comes back and the logic in the destroyed component will be executed. That execution is now considered out-of-context and can result in many things including very low performance and data/state corruption.
因此,最佳实践是使用takeUntil(),并在组件销毁时从http调用订阅中取消订阅。
注意:
RxJS不是Angular特有的
模板中使用的异步管道会在销毁时自动取消订阅
取消订阅多次没有负面影响,除了额外的无操作呼叫
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
}
}