如何在Angular 4+中取消/中止所有待处理的HTTP请求。
有一个"unsubscribe"方法可以取消HTTP请求,但如何一次性取消所有待处理的请求。
尤其是在路由更改时。
我做过一件事
ngOnDestroy() {
this.subscription.unsubscribe();
}
但如何在全球范围内实现这一目标?
有什么想法吗?
如何在Angular 4+中取消/中止所有待处理的HTTP请求。
有一个"unsubscribe"方法可以取消HTTP请求,但如何一次性取消所有待处理的请求。
尤其是在路由更改时。
我做过一件事
ngOnDestroy() {
this.subscription.unsubscribe();
}
但如何在全球范围内实现这一目标?
有什么想法吗?
使用RxJS中的takeUntil()
运算符全局取消订阅:
- RxJS 6+(使用pipe
语法)
import { takeUntil } from 'rxjs/operators';
export class YourComponent {
protected ngUnsubscribe: Subject<void> = new Subject<void>();
[...]
public httpGet(): void {
this.http.get()
.pipe( takeUntil(this.ngUnsubscribe) )
.subscribe( (data) => { ... });
}
public ngOnDestroy(): void {
// This aborts all HTTP requests.
this.ngUnsubscribe.next();
// This completes the subject properlly.
this.ngUnsubscribe.complete();
}
}
- RxJS < 6
import 'rxjs/add/operator/takeUntil'
export class YourComponent {
protected ngUnsubscribe: Subject<void> = new Subject<void>();
[...]
public httpGet(): void {
this.http.get()
.takeUntil(this.ngUnsubscribe)
.subscribe( (data) => { ... })
}
public ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
在你想要完成一堆流时,可以使用next()
在取消订阅的Subject
上发出事件。此外,在组件销毁时取消订阅处于活动状态的Observables以避免内存泄漏也是一个好习惯。
值得阅读的文章:
你可以创建一个拦截器来对每个请求应用takeUntil
操作符。然后在路由改变时,您将发出事件来取消所有待处理的请求。
@Injectable()
export class HttpCancelInterceptor implements HttpInterceptor {
constructor(private httpCancelService: HttpCancelService) { }
intercept<T>(req: HttpRequest<T>, next: HttpHandler): Observable<HttpEvent<T>> {
return next.handle(req).pipe(takeUntil(this.httpCancelService.onCancelPendingRequests()))
}
}
辅助服务。
@Injectable()
export class HttpCancelService {
private cancelPendingRequests$ = new Subject<void>()
constructor() { }
/** Cancels all pending Http requests. */
public cancelPendingRequests() {
this.cancelPendingRequests$.next()
}
public onCancelPendingRequests() {
return this.cancelPendingRequests$.asObservable()
}
}
在您的应用程序中的某个地方(例如在appComponent的onInit中),挂钩路由更改。
this.router.events.subscribe(event => {
if (event instanceof ActivationEnd) {
this.httpCancelService.cancelPendingRequests()
}
})
最后但同样重要的是,将拦截器注册到你的app.module.ts中:
import { HttpCancelInterceptor } from 'path/to/http-cancel.interceptor';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
@NgModule({
[...]
providers: [
{
multi: true,
provide: HTTP_INTERCEPTORS,
useClass: HttpCancelInterceptor
}
],
[...]
})
export class AppModule { }
export function AutoUnsubscribe(constructor) {
const original = constructor.prototype.ngOnDestroy;
constructor.prototype.ngOnDestroy = function() {
for (const prop in this) {
if (prop) {
const property = this[prop];
if (property && (typeof property.unsubscribe === 'function')) {
property.unsubscribe();
}
}
}
if (original && typeof original === 'function') {
original.apply(this, arguments)
};
};
}
那么你可以在你的组件中将它用作装饰器
@AutoUnsubscribe
export class YourComponent {
}
但是您仍需要将订阅存储为组件属性。当您导航离开组件时,AutoUnsubscribe函数将发生。
(Array.isArray(property) ? property : [property]).filter(property => isFunction(property.unsubscribe)).forEach(property => property.unsubscribe()));
。 - Aluan Haddadimport {Http, Request, RequestOptions, RequestOptionsArgs, Response} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import {Subscription} from 'rxjs/Subscription';
export default interface CancellationAwareHttpClient extends Http { }
export default class CancellationAwareHttpClient {
constructor(private wrapped: Http) {
const delegatedMethods: Array<keyof Http> = [
'get', 'post', 'put', 'delete',
'patch', 'head', 'options'
];
for (const key of delegatedMethods) {
this[key] = wrapped[key].bind(wrapped);
}
}
cancelOutstandingRequests() {
this.subscriptions.forEach(subscription => {
subscription.unsubscribe();
});
this.subscriptions = [];
}
request(url: string | Request, options?: RequestOptionsArgs) {
const subscription = this.wrapped.request(url, options);
this.subscriptions.push(subscription);
return subscription;
}
subscriptions: Subscription[] = [];
}
CancellationAwareHttpClient
的 interface
和 class
声明已经合并了。这样,我们的类通过 interface
声明中的 extends
子句来实现 Http
。import {NgModule} from '@angular/core';
import {ConnectionBackend, RequestOptions} from '@angular/http';
import CancellationAwareHttpClient from 'app/services/cancellation-aware-http-client';
let cancellationAwareClient: CancellationAwareHttpClient;
const httpProvider = {
provide: Http,
deps: [ConnectionBackend, RequestOptions],
useFactory: function (backend: ConnectionBackend, defaultOptions: RequestOptions) {
if (!cancellationAwareClient) {
const wrapped = new Http(backend, defaultOptions);
cancellationAwareClient = new CancellationAwareHttpClient(wrappedHttp);
}
return cancellationAwareClient;
}
};
@NgModule({
providers: [
// provide our service as `Http`, replacing the stock provider
httpProvider,
// provide the same instance of our service as `CancellationAwareHttpClient`
// for those wanting access to `cancelOutstandingRequests`
{...httpProvider, provide: CancellationAwareHttpClient}
]
}) export class SomeModule {}
Http
的任何服务或组件的情况下进行实验。 - Aluan HaddadwrappedHttp
?你的评论没有澄清这一点。你能和我们分享更多细节吗?谢谢。 - Dom试一试:
import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs/Rx';
export class Component implements OnInit, OnDestroy {
private subscription: Subscription;
ngOnInit() {
this.subscription = this.route.params.subscribe();
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
在 @Bladito 的回答上补充一点,他的回答已经非常完美。
实际上,HttpCancelService 堆栈是完美的,但问题在于它的调用位置。如果您有子路由,在导航结束时调用此函数可能会导致问题。
因此,我创建了一个抽象容器组件,在销毁时调用 HttpCancelService。这样我就可以更细致地管理何时切断任何 Http 取消请求。
import { Component, OnDestroy, OnInit } from '@angular/core';
import { HttpCancelService } from '../../services/http-cancel-service.service';
@Component({
selector: 'some-abstract-container',
template: `
ABSTRACT COMPONENT
`,
styleUrls: ['./abstract-container.component.scss']
})
export class AbstractContainerComponent implements OnInit, OnDestroy {
constructor(protected readonly httpCancelService: HttpCancelService) {}
ngOnInit() {}
ngOnDestroy(): void {
this.httpCancelService.cancelPendingRequests();
}
}
这是一个扩展抽象组件的具体组件:
import { Component, OnInit } from '@angular/core';
import { AbstractContainerComponent } from '../../../shared/components/abstract-container/abstract-container.component';
import { HttpCancelService } from '../../../shared/services/http-cancel-service.service';
@Component({
selector: 'some-concrete-container',
templateUrl: '.some-concrete-container.component.html',
styleUrls: ['./some-concrete-container.component.scss']
})
export class SomeConcreteContainerComponent extends AbstractContainerComponent implements OnInit {
constructor(protected readonly httpCancelService: HttpCancelService) {
super(httpCancelService);
}
ngOnInit() {}
}
get
请求,因为其所在的组件即将被销毁。
如果您想让生活变得更轻松,则可以使用until-destroy。它将在组件即将销毁(ngOnDestroy
)时自动取消订阅所有可观察对象。
它足够细粒度且更通用(不仅限于 Http 请求,而是所有的 observables
都将被取消订阅)。
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
@UntilDestroy()
@Component({})
export class InboxComponent {
ngOnInit() {
interval(1000)
.pipe(untilDestroyed(this))
.subscribe();
}
}
//This is the example of cancelling the get request once you leave the TestComponent.
import { Component, OnInit} from '@angular/core';
@Component({
selector: 'app-test',
templateUrl: './test.component.html'
})
export class TestComponent implements OnInit {
request: any;
someList: any;
constructor( private _someService: SomeService) {
}
ngOnInit() {
this.getList();
}
ngOnDestroy(){
this.request.unsubscribe(); // To cancel the get request.
}
getList() {
this.request= this._someService.getAll()
.subscribe((response: any) => {
this.someList= response;
}, (error) => {
console.log("Error fetching List", error);
})
}
}