【问题标题】:Keeping error information and the outer observable alive保持错误信息和外部 observable 活着
【发布时间】:2020-10-07 14:07:14
【问题描述】:

为确保错误不会完成 outer observable,我采用的常见rxjs effects 模式是:

 public saySomething$: Observable<Action> = createEffect(() => {

    return this.actions.pipe(

      ofType<AppActions.SaySomething>(AppActions.SAY_SOMETHING),

      // Switch to the result of the inner observable.
      switchMap((action) => {
        // This service could fail.
        return this.service.saySomething(action.payload).pipe(
          // Return `null` to keep the outer observable alive!
          catchError((error) => {
            // What can I do with error here?
            return of(null);
          })
        )
      }),

      // The result could be null because something could go wrong.
      tap((result: Result | null) => {
        if (result) {
          // Do something with the result!
        }
      }),

      // Update the store state.
      map((result: Result | null) => {
        if (result) {
          return new AppActions.SaySomethingSuccess(result);
        }
        // It would be nice if I had access the **error** here. 
        return new AppActions.SaySomethingFail();
      }));
});

请注意,如果底层网络调用失败 (service.saySomething(action.payload)),我在 inner observable 上使用 catchError 来保持 outer observable 活动: p>

catchError((error) => {
  // What can I do with error here?
  return of(null);
})

随后的tapmap 运算符通过允许null(即(result: Result | null))在其签名中适应这一点。但是,我丢失了错误信息。最终,当最终的 map 方法返回 new AppActions.SaySomethingFail(); 时,我丢失了有关错误的任何信息。

我怎样才能在整个管道中保留错误信息,而不是在被捕获时丢失它?

【问题讨论】:

  • 你就不能return of(error);吗?
  • @józef-podlecki 我可以,但是我如何在tapmap 中区分错误和成功结果?
  • 这似乎会导致错误tap((result: Result | null) =&gt; result is Result { 是警告A function whose declared type is neither 'void' nor 'any' must return a value.ts(2355)

标签: rxjs ngrx ngrx-effects rxjs-observables


【解决方案1】:

按照 cmets 的建议,您应该使用 Type guard function

很遗憾,我无法在 sn-p 中运行 typescript,所以我注释了 types

const { of, throwError, operators: {
    switchMap,
    tap,
    map,
    catchError
  }
} = rxjs;

const actions = of({payload: 'data'});

const service = {
  saySomething: () => throwError(new Error('test'))
}

const AppActions = {
}

AppActions.SaySomethingSuccess = function () {
}
AppActions.SaySomethingFail = function() {
}

/* Type guard */
function isError(value/*: Result | Error*/)/* value is Error*/ {
  return value instanceof Error;
}

const observable = actions.pipe(
  switchMap((action) => {
    
    return service.saySomething(action.payload).pipe(
      catchError((error) => {
        return of(error);
      })
    )
  }),
  tap((result/*: Result | Error*/) => {
    if (isError(result)) {
      console.log('tap error')
      return;
    }
    
    console.log('tap result');
  }),
  map((result/*: Result | Error*/) => {
    if (isError(result)) {
      console.log('map error')
      return new AppActions.SaySomethingFail();
    }
    
    console.log('map result');
    return new AppActions.SaySomethingSuccess(result);
  }));
  
  observable.subscribe(_ => {

  })
&lt;script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"&gt;&lt;/script&gt;

【讨论】:

  • 谢谢。看起来它会完成这项工作,但它似乎不是很有功能或类似ngrx。我很惊讶它归结为检查变量的类型,考虑到我知道在错误被捕获的那一刻,我有一个错误而不是一个有效的结果。另一个问题是错误通常具有any 类型,这意味着我需要构造一个新的Error 对象,以便实例可靠地工作。这开始让人觉得很奇怪。
  • 我注意到,由于tapmap 的参数现在可以是ResultError,编译器抱怨该值可能不确定(Result)属性,即使在检查它是否是 Error 的实例之后。
  • Result 是如何定义的?
  • 这是一个接口; interface Result { ... }。我的类型保护没有返回类型谓词,所以编译器抱怨。在确保返回类型为value is Error 后,编译器很高兴。
【解决方案2】:

我不会尝试在整个管道中保留错误信息。相反,您应该将成功管道(tapmap)与错误管道(catchError)分开,方法是将所有运算符添加到它们应该实际使用其结果的可观察对象,即您的内部可观察对象。

public saySomething$: Observable<Action> = createEffect(() => {

    return this.actions.pipe(
      ofType<AppActions.SaySomething>(AppActions.SAY_SOMETHING),
      switchMap((action) => this.service.saySomething(action.payload).pipe(
        tap((result: Result) => {
          // Do something with the result!
        }),
        // Update the store state.
        map((result: Result) => {
          return new AppActions.SaySomethingSuccess(result);
        }),
        catchError((error) => {
          // I can access the **error** here. 
          return of(new AppActions.SaySomethingFail());
        })
      )),     
    );
});

这种方式tapmap 只会在this.service.saySomething 的成功结果上执行。将所有错误副作用和错误映射移至catchError

【讨论】:

    猜你喜欢
    • 2021-02-13
    • 2019-05-20
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多