Is it possible to throw errors inside ngrx-effects without completing the Observable stream?

人盡茶涼 提交于 2019-12-10 10:56:07

问题


Is there any way to use throw with an Error object inside ngrx-effects streams without completing the stream?

I've read these great answers on why the stream is being killed by throwing an error:

@ngrx Effect does not run the second time

ngrx effects error handling

https://github.com/ngrx/platform/issues/646

My question is if I'm implementing the Angular ErrorHandler to catch errors, if I'm going to be able to use that with ngrx effects.

@Effect()
  loginUserEffect: Observable<loginActions.Actions> = this.actions$
    .ofType(loginActions.LOGIN_USER)
    .map((action: loginActions.LoginUser) => action.payload)
    .mergeMap(payload => {
      return this.loginService
        .authenticate(payload)
        .map(response => new loginActions.LoginUserSuccess(response))
        .catch((error: HttpErrorResponse) =>
          of(new loginActions.LoginUserFailure(error))
        )
  })

  @Effect({ dispatch: false })
  loginUserEffectSuccess$ = this.actions$
    .ofType(loginActions.LOGIN_USER_SUCCESS)
    .do(() => this.router.navigate(['/account-home']))

  @Effect({ dispatch: false })
  loginUserEffectFailure$ = this.actions$
    .ofType(loginActions.LOGIN_USER_FAILURE)
    .map((action: loginActions.LoginUserFailure) => {
      throw action.payload // Stream completes
  })

I imagine I could create some way of dealing with errors that doesn't involve throwing anything, but wanted to make sure I needed to go that route or if there was a way to keep both of them peacefully coexisting.

Currently in my class that implements ErrorHander, I have this:

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  private messagesService: MessagesService
  private router: Router

  constructor(
    private injector: Injector, // DI workaround (https://stackoverflow.com/a/41585902)
    private errorLoggerService: ErrorLoggerService
  ) {
    // DI workaround (https://stackoverflow.com/a/41585902)
    setTimeout(() => (this.messagesService = injector.get(MessagesService)))
    setTimeout(() => (this.router = injector.get(Router)))
  }

  handleError(error) {
    if (error instanceof HttpErrorResponse) {
      this.handleServerError(error)
    } else if (error instanceof ClientError) {
      this.handleClientError(error)
    } else {
      this.handleUnexpectedError(error)
    }
  }

Which means I just throw errors and they are handled based on the type


回答1:


If you want to throw an error that will not be caught by the observable and will instead be reported to a global error handler, you have throw the error from outside of the observable's call stack.

You could do something like this:

@Effect({ dispatch: false })
loginUserEffectFailure$ = this.actions$
  .ofType(loginActions.LOGIN_USER_FAILURE)
  .do(action => setTimeout(() => { throw action.payload; }, 0))
})

However, as your effects are in a class that already uses Angular's DI mechanism, I would suggest you instead inject your GlobalErrorHandler and call it directly.

Without the setTimeout calls I think it would be clearer and easier to test, too.




回答2:


I ended up creating an Error action:

@Effect()
  loginUserEffectFailure$: Observable<
    errorsActions.Actions
  > = this.actions$
    .ofType(loginActions.LOGIN_USER_FAILURE)
    .map(
      (action: loginActions.LoginUserFailure) =>
        new errorsActions.Error(action.payload)
    )

and having that call the error handler in a dedicated effect:

@Effect({ dispatch: false })
  errorEffect$ = this.actions$
    .ofType(errorsActions.ERROR)
    .map((action: errorsActions.Error) =>
      this.clientErrorHandler.handleError(action.payload)
    )

I left the global error handler just for unexpected exceptions to be caught and logged.



来源:https://stackoverflow.com/questions/47896171/is-it-possible-to-throw-errors-inside-ngrx-effects-without-completing-the-observ

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!