保持错误信息和外部 observable 活动

Keeping error information and the outer observable alive

为了确保错误不会完成 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();
      }));
});

请注意,我在 inner observable 上使用 catchError 以保持 outer observable 在底层网络时保持活动状态呼叫失败(service.saySomething(action.payload)):

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

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

如何在整个管道中保留错误信息而不是在它被捕获时丢失它?

根据评论中的建议,您应该使用 Type guard function

不幸的是,我无法在片段中 运行 打字稿,所以我评论了类型

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(_ => {

  })
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.5/rxjs.umd.js"></script>

我不会尝试在整个管道中保留错误信息。相反,你应该将你的成功管道(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.