我需要在切换到其他路线时取消订阅请求。在我看来,无论请求发送到哪里(解析器,组件)。在我的代码中,请求的基本发送由解析器执行
@Injectable()
export class RoomsResolve implements Resolve<boolean> {
constructor(
private roomsService: RoomsService,
private store: Store<fromRooms.IRoomsState>,
private resolveRequestUnsubscribeService: ResolveRequestUnsubscribeService
) {}
public resolve(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
this.store.select(fromRooms.getRoomsLoaded).pipe(
take(1)
).subscribe((loaded) => {
if (!loaded) {
this.store.dispatch(new fromRooms.GetRoomList({
requestParams: {
lpuId: 204
},
requestOptions: {
unsubscribe: this.resolveRequestUnsubscribeService.createUnsubscriber(state.url, 'getRoomList')
}
}));
}
});
return true;
}
}
这里我们发送一个动作来请求来自后端服务的数据,并为请求创建一个取消器
以下服务负责取消者的操作:
import { startWith } from 'rxjs/operator/startWith';
import { Injectable } from '@angular/core';
import { ActivatedRoute, NavigationStart, Router, RouterEvent,
RoutesRecognized } from '@angular/router';
import { Subject } from 'rxjs/Subject';
import * as moment from 'moment';
import * as _ from 'lodash';
export interface IUnsubscribeCache {
[name: string]: IUnsubscribeCacheItem;
}
export interface IUnsubscribeCacheItem {
liveTime: number;
unsubscribers: {[name: string]: Subject<boolean>};
}
@Injectable()
export class ResolveRequestUnsubscribeService {
public unsubscribeCache: IUnsubscribeCache = {};
private unsubscriberLiveTime: number = 60000;
constructor(
private router: Router,
private activatedRoute: ActivatedRoute
) {
//
console.log('INIT_ResolveRequestUnsubscribeService');
this.watchChangeURL();
}
private watchChangeURL(): void {
this.router.events
.filter((e) => e instanceof RoutesRecognized)
.pairwise()
.subscribe((event: any[]) => {
const prevPath: string = event[0].urlAfterRedirects;
!!prevPath && this.unsubscribeRequests(this.convertPathToUnsubscribersGroupName(prevPath));
console.log('PREV_URL', event, event[0].urlAfterRedirects);
});
}
private removeUnfulfilledUnsubscribers(): void {
const unfulfilledUnsubscribers: string[] = [];
const now = moment().valueOf();
_.forIn(this.unsubscribeCache, (value: IUnsubscribeCacheItem, key) => {
if (value.liveTime + this.unsubscriberLiveTime <= now) {
unfulfilledUnsubscribers.push(key);
}
});
_.omit(this.unsubscribeCache, unfulfilledUnsubscribers);
}
public createUnsubscriber(path: string, methodName?: string): Subject<boolean> {
this.removeUnfulfilledUnsubscribers();
const groupName = this.convertPathToUnsubscribersGroupName(path);
const unsubscribersGroup = this.unsubscribeCache[groupName];
!unsubscribersGroup && this.addUnsubscribersGroupToCache(groupName);
this.unsubscribeCache[groupName].unsubscribers[methodName] = new Subject<boolean>();
return this.unsubscribeCache[groupName].unsubscribers[methodName];
}
private convertPathToUnsubscribersGroupName(path: string): string {
return path.replace('/', '_');
}
private addUnsubscribersGroupToCache(groupName: string): void {
this.unsubscribeCache[groupName] = {
liveTime: moment().valueOf(),
unsubscribers: {}
};
}
private unsubscribeRequests(name: string): void {
this.removeUnfulfilledUnsubscribers();
const cache: IUnsubscribeCacheItem = this.unsubscribeCache[name];
if (cache) {
if (_.size(cache.unsubscribers) > 0) {
_.forIn(this.unsubscribeCache[name].unsubscribers, (value: Subject<boolean>, key: string) => {
value.next(false);
value.complete();
});
}
_.omit(this.unsubscribeCache, name);
}
}
}
工作的逻辑很简单:创建缓存,当我们转到路由并执行请求,将缓存写入缓存并将其发送到请求,在转换到另一个路由期间,我们取消所有请求从以前的路线。
对于http请求实现了包装器:
import * as _ from 'lodash';
import { Config } from '../../services/config/config';
import { ConfigService } from '../../services/config/config.service';
import { RepositoryError } from './repository-error.model';
import { IRepositoryCachePromise } from './repository-cache-
promise.interface';
import { IRepositoryRequestParams } from './repository-request-
params.interface';
import { HttpClient, HttpClientModule, HttpErrorResponse, HttpHeaders,
HttpResponse } from '@angular/common/http';
import { Subject } from 'rxjs/Subject';
import { AsyncSubject } from 'rxjs/AsyncSubject';
export class Repository {
protected config: Config = null;
protected connectorUrl: string = '';
protected cachePromisses: IRepositoryCachePromise[] = [];
public constructor(
protected http: HttpClient,
protected configService: ConfigService
) {
//
}
protected request(params: IRepositoryRequestParams): Promise<any> {
const url: string = this.getConnectorUrl(params.action);
return this.http.request(
params.method,
url,
{
body: JSON.stringify(params.data),
headers: {
'Content-Type': 'application/json',
Authorization2: params.options.user.getAuthInfo().getAuthToken(),
lpu_id: params.options.user.getUserInfo().getCurrentHospital().getPlaceID(),
user_role_id: params.options.user.getUserInfo().getCurrentRole().getID(),
job_id: params.options.user.getUserInfo().getCurrentJob().getContractID(),
speciality_code: params.options.user.getUserInfo().getCurrentJob().getID(),
available_resource_id: params.options.user.getUserInfo().getCurrentJob().getCurrentResource()
? params.options.user.getUserInfo().getCurrentJob().getCurrentResource().getID() : ''
}
})
.takeUntil(params.options.unsubscribe || new Subject<boolean>())
.toPromise()
.then((response) => {
const isError: boolean = this.hasError(response);
return isError ? Promise.reject(response) : response;
})
.catch((error: HttpErrorResponse) => Promise.reject(this.getError(error)));
}
private hasError(response): boolean {
return !response.success && (response.error
|| (response.warnings && response.warnings.length) || response.errorMessage);
}
private getError(response): RepositoryError {
return new RepositoryError().parse(response);
}
protected getConnectorUrl(action: string = ''): string {
if (!this.config) {
this.getConfig();
}
return `${this.config.getApiUrl(this.connectorUrl)}/${action}.api`;
}
private getConfig(): void {
this.config = this.configService.getConfig();
}
public getCacheKey(url: string, params: any): string {
params = _.cloneDeep(params);
if (_.has(params, 'options.user.userInfo')) {
delete params.options.user.userInfo.contracts;
delete params.options.user.userInfo.nativeUserInfo;
delete params.options.user.userInfo.workplaces;
delete params.options.user.userInfo.roles;
}
if (_.has(params, 'options.user.userInfo.currentJob')) {
delete params.options.user.userInfo.currentJob.resources;
}
if (_.has(params, 'options.user.userInfo.currentRole')) {
delete params.options.user.userInfo.currentRole.allowRights;
delete params.options.user.userInfo.currentRole.disallowRights;
}
if (_.has(params, 'options')) {
delete params.options.forced;
delete params.options.cacheLifeTime;
}
return url + '?' + JSON.stringify(params);
}
}
要取消请求,我尝试使用方法.takeUntil(params.options.unsubscribe || new Subject<boolean>())
,从ResolveRequestUnsubscribeService传递取消器
当我尝试在控制台中取消请求时,出现错误:
core.js:1427 ERROR错误:未捕获(在承诺中):TypeError:无法分配给对象'[object Object]'的只读属性'isStopped' TypeError:无法分配给对象'[object Object]'
的只读属性'isStopped'
错误导致此行:
value.complete();
ResolveRequestUnsubscribeService - &gt; unsubscribeRequests
请帮助我理解我做错了什么
抱歉我的英文。
我无法创建可行的示例