我在NgRx中遇到竞争条件问题。在下面的示例中,我将在启动异步远程操作的同时异步呈现一个加载对话框。但是,远程操作有可能在加载对话框完全构建之前完成并激发disseLoadingDialog((,从而导致控制台错误。
在远程操作开始之前,NgRx中完成presentalLoadingDialog((的好策略是什么?
@Effect() fetchServerData$ = this.actions$.pipe(
ofType<FetchServerData>(ActionTypes.FetchServerData),
switchMap(action => {
this.presentLoadingDialog('...loading');
return this.dataService.fetchData(action.payload).pipe(
map(result => {
this.dismissLoadingDialog();
return new FetchServerDataSuccess(result);
}),
catchError(err => of(new FetchServerDataFail(err)))
);
})
);
async presentLoadingDialog(message: string): Promise<void> {
this.isLoading = true;
return this.loadingCtrl
.create({
duration: 5000,
message: message
})
.then(loadingDialog => {
loadingDialog.present().then(() => {
if (!this.isLoading) {
loadingDialog.dismiss();
}
});
});
}
async dismissLoadingDialog() {
this.isLoading = false;
if (!isNullOrUndefined(this.loadingCtrl)): Promise<boolean> {
return this.loadingCtrl.dismiss();
}
}
create
方法返回一个Promise,该Promise在加载程序创建完成时解析。因此,你可以在你的效果的可观察链中使用它:
presentLoadingDialog(message: string) {
const loader = this.loadingCtrl
.create({
duration: 5000,
message: message
});
return loader.present();
}
dismissLoadingDialog() {
this.loadingCtrl.dismiss();
}
@Effect() fetchServerData$ = this.actions$.pipe(
ofType<FetchServerData>(ActionTypes.FetchServerData),
switchMap(action => forkJoin(from(this.presentLoadingDialog('...loading'), of(action)),
switchMap(([_, action]) => this.dataService.fetchData(action.payload).pipe(
tap(() => this.dismissLoadingDialog()),
map(result => new FetchServerDataSuccess(result)),
catchError(err => {
this.dismissLoadingDialog();
return of(new FetchServerDataFail(err))
})
))
);
我看到的标准是在您的状态中加载和加载标志。当您调度一个加载操作时,reducer会在该操作触发http请求之前用loading:true和loaded:false更新状态。然后,操作切换到一个操作,该操作使用响应和loading:false以及loaded:true更新状态。
在您的组件中,您有一个加载标志的选择器,并订阅它来打开和关闭对话框
this.loadingSub = loadings$.subscribe(loading => {
if (loading) {
this.presentLoadingDialog('...loading');
} else {
this.loadingDialog.dismiss();
}
});
取消订阅onDestroy
应该由您的组件来显示UI组件,我认为调用加载对话框的操作不是一个操作问题。我不建议使用状态管理的核心来调用UI组件。