首页 文章

NgRx - store.dispatch在reducer抛出异常后停止工作

提问于
浏览
1

在我的一个Reducer抛出异常后,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

我看到一些与处理Error中的错误有关的答案,但我根本没有使用Effects . 在reducer抛出异常后,有没有办法让商店恢复到一致状态?

我使用的是Angular 5.2.9和ngrx 4.1.1

3 回答

  • 1

    这可能不是你问题的答案,但你不应该在你的减速器中抛出错误 .

    减速器必须是 pure .

  • 0

    当从订阅者中选择的订阅者在其订阅处理程序中抛出异常时,也会发生这种情况(根据我的经验),因此即使您的纯粹Reducer从不抛出异常,也可能最终处于此状态 .

    在我们的例子中,升级到Angular 6 / NGRX 6似乎解决了这个问题 . 我建议升级到两者的v6,然后再次测试,看它是否仍然发生 .

  • 1

    我能够使用元减速器和服务使其工作 .

    您使用注入令牌添加具有Angular服务作为依赖项的元减少器 . meta-redurs将reducer包装在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抛出异常,这将确保存储继续工作,并且它还提供了一种方法来获取解析或失败的promise,具体取决于调度给定操作的结果 .

相关问题