我有一个关于@ngrx效果的非常基本的问题:如何忽略在执行效果期间发生的错误,使其不会影响未来的效果执行?
我的情况如下:我有一个动作(LOGIN)和一个听取该动作的效果。如果在此效果中发生错误,我想忽略它。在此错误后第二次调度LOGIN时,应该再次执行该效果。
我的第一次尝试是:
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.flatMap(async () => {
console.debug('LOGIN');
// throw an error
let x = [];x[0]();
})
.catch(err => {
console.error('Error at login', err);
return Observable.empty();
});
第一次调度LOGIN并按预期捕获错误。但是,如果我之后第二次发送LOGIN,则没有任何反应;效果不会被执行。
因此我尝试了以下内容:
.catch(err => {
return this.login$;
});
,但这导致无限循环...你知道如何在不阻止效果执行的情况下捕获错误吗?
答案 0 :(得分:19)
ngrx
基础设施通过使用NgModule
导入应用EffectsModule.run
的提供商订阅效果。
当可观察的错误和catch
返回一个空的obervable时,组合的observable完成并且其订阅者被取消订阅 - 这是class path的一部分。这就是您认为没有进一步处理LOGIN
动作的原因。该效果在完成时取消订阅,ngrx
基础架构不会重新订阅。
通常,您会在flatMap
(现在称为mergeMap
)内进行错误处理:
import { Actions, Effect, toPayload } from "@ngrx/effects";
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.map(toPayload)
.flatMap(payload => Observable
.from(Promise.reject('Boom!'))
.catch(error => {
console.error('Error at login', error);
return Observable.empty();
})
});
组成内部observable的catch
将看到一个空的observable展平/合并到效果中,因此不会发出任何动作。
答案 1 :(得分:4)
发生错误时,@Effect
流正在完成,从而阻止任何进一步的操作。
解决方案是切换到一次性流。如果在一次性流中发生错误,则可以,因为主@Effect
流始终保持活动状态,并且将来的操作将继续执行。
@Effect()
login$ = this.actions$
.ofType('LOGIN')
.switchMap(action => {
// This is the disposable stream!
// Errors can safely occur in here without killing the original stream
return Rx.Observable.of(action)
.map(action => {
// Code here that throws an error
})
.catch(error => {
// You could also return an 'Error' action here instead
return Observable.empty();
});
});
此博客文章中有关此技术的更多信息:The Quest for Meatballs: Continue RxJS Streams When Errors Occur
答案 2 :(得分:1)
这就是我如何处理在没有找到任何数据的Observable上通知或不通知的选项:
public listenCampaignValueChanged(emitOnEmpty: boolean = false): Observable<CampaignsModelExt> {
var campaignIdSelected$ = this.ngrxStore.select(store => store.appDb.uiState.campaign.campaignSelected)
var campaigns$ = this.ngrxStore.select(store => store.msDatabase.sdk.table_campaigns);
return campaignIdSelected$
.combineLatest(campaigns$, (campaignId: number, campaigns: List<CampaignsModelExt>) => {
return campaigns.find((i_campaign: CampaignsModelExt) => {
return i_campaign.getCampaignId() == campaignId;
});
}).flatMap(v => (v ? Observable.of(v) : ( emitOnEmpty ? Observable.of(v) : Observable.empty())));
}