NgRx-减速器抛出异常后store.dispatch停止工作

时间:2018-08-09 19:34:59

标签: ngrx ngrx-store

我的一个化简器引发异常后,store.dispatch方法停止工作。例如:

 function Reducer(currentState, action){
   switch(action.type){
      case 'BLOW':
        throw "BANG!"
      case 'TEST':
        console.log('OK');
        return currentState;
  }

我发送了两次“ TEST”,一切都很好,即打印了“ OK”。然后我发了“ BLOW”,这引发了异常。之后,当我再次发送“ TEST”时,什么也没有发生。该动作永远不会到达减速器。

enter image description here

我看到了一些与处理Effects内部错误有关的答案,但我根本没有使用Effects。在reducer引发异常之后,是否有办法使商店恢复一致状态?

我正在使用Angular 5.2.9和ngrx 4.1.1

2 个答案:

答案 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抛出异常,商店将继续工作,并且还提供了一种方式来获得承诺,该承诺将根据调度给定操作的结果来解决或失败。