我的一个化简器引发异常后,store.dispatch方法停止工作。例如:
function Reducer(currentState, action){
switch(action.type){
case 'BLOW':
throw "BANG!"
case 'TEST':
console.log('OK');
return currentState;
}
我发送了两次“ TEST”,一切都很好,即打印了“ OK”。然后我发了“ BLOW”,这引发了异常。之后,当我再次发送“ TEST”时,什么也没有发生。该动作永远不会到达减速器。
我看到了一些与处理Effects内部错误有关的答案,但我根本没有使用Effects。在reducer引发异常之后,是否有办法使商店恢复一致状态?
我正在使用Angular 5.2.9和ngrx 4.1.1
答案 0 :(得分:1)
(根据我的经验)当从商店中进行选择的订阅者在其订阅处理程序中引发异常时,也会发生这种情况,因此,即使您的纯reducer从不抛出异常,您也可以最终处于这种状态。
在我们的案例中,升级到Angular 6 / NGRX 6似乎可以解决此问题。我建议升级到两者的v6,然后再次测试以查看是否仍然存在。
答案 1 :(得分:1)
我能够使用meta-reducer和服务使其工作。
您使用注入令牌添加具有Angular服务作为依赖项的meta归约器。元化约简器将约简器包装在try / catch块周围,并让服务知道调度该动作的结果。
@NgModule({
providers: [
{
provide: META_REDUCERS,
deps: [StoreExceptionCatcher],
useFactory: getMetaReducers
}
]
})
export class AppModule {}
export function getMetaReducers(storeExceptionCatcher: StoreExceptionCatcher): MetaReducer<any>[] {
/**
* Guarantees store will still be in a consistent state if reducers throw an exception.
* Notifies StoreExceptionCatcher of the result of dispatching a given action.
*/
function exceptionCatcher(reducer: ActionReducer<any>): ActionReducer<any> {
return function(state, action) {
try{
state = reducer(state, action);
storeExceptionCatcher.sendResult(action);
}catch(err){
storeExceptionCatcher.sendResult(action, err);
}
/* simply returns the old state if action throws an exception */
return state;
};
}
return [exceptionCatcher];
}
服务的外观如下:
import { Injectable } from '@angular/core';
import { BehaviorSubject, Subject } from 'rxjs';
@Injectable()
export class StoreExceptionCatcher{
private resultObs: Subject<DispatchResult> = new Subject<DispatchResult>();
/**
* Returns a promise that will resolve or fail after 'action' is dispatched to the store.
* The object passed to this function should be the same as the one passed to store.dispatch.
* This function should be called before 'store.dispatch(action)'.
*/
waitForResult(action: any): Promise<void>{
return this.resultObs.filter(result => result.action === action).take(1).toPromise().then(res => {
if(res.error){
throw res.error
}
});
}
/** Should only be used by meta-reducer */
sendResult(action: any, error?: any): void{
this.resultObs.next({action, error});
}
}
export interface DispatchResult{
action: any,
error?: any;
}
现在,要将操作发送到商店,您可以执行以下操作:
private dispatch(action: any): Promise<void>{
const promise = this.storeExceptionCatcher.waitForResult(action);
this.store.dispatch(action);
return promise;
}
这将确保如果reducer抛出异常,商店将继续工作,并且还提供了一种方式来获得承诺,该承诺将根据调度给定操作的结果来解决或失败。