如何从失败的 forkJoin 请求中获取数据?

How to get data from failed forkJoin request?

使用 Angular Rxjsngrx

我有一个调度 4 API 的操作,我正在执行以下操作 =>

  @Effect()
  getAllModels$ = this.actions$.pipe(
    ofType<featureActions.GetAllModelsRequest>(featureActions.ActionTypes.GetAllModelsRequest),
    switchMap((action) =>
      forkJoin([
        this.dataService.GetAllModelFromServer(),
        this.dataService.GetAllModelFromHost(),
        this.dataService.GetAllModelFromCache(),
        this.dataService.GetAllModelFromPreference(),
      ]).pipe(
        map(
          ([server, host, cache, preference]) =>
            new featureActions.GetAllModelsSuccess({
              //...
            })
        ),
        catchError((error: HttpErrorResponse) => {
          return of(new featureActions.GetAllModelsFailed({ error: error.message }));
        })
      )
    )
  );

问题是,当其中一个 API 失败时,一切都失败了,我处于失败行动中。所有检索到的数据(在一个端点失败之前)都丢失了。

有没有办法获取 catchError 中检索到的数据,或者唯一的解决方案是将 api 一个接一个地链接起来?

您可以编写自己的 forkJoin 实现。这是一个源自原始 (https://github.com/ReactiveX/rxjs/blob/master/src/internal/observable/forkJoin.ts) 的简单示例:

export function forkJoin2(...args: any[]): Observable<any> {
  const resultSelector = popResultSelector(args);

  const { args: sources, keys } = argsArgArrayOrObject(args);

  if (resultSelector) {
    // deprecated path.
    return forkJoinInternal(sources, keys).pipe(map((values: any[]) => resultSelector!(...values)));
  }

  return forkJoinInternal(sources, keys);
}

function forkJoinInternal(sources: ObservableInput<any>[], keys: string[] | null): Observable<any> {
  return new Observable((subscriber) => {
    const len = sources.length;
    if (len === 0) {
      subscriber.complete();
      return;
    }
    const values = new Array(len);
    let completed = 0;
    let emitted = 0;
    for (let sourceIndex = 0; sourceIndex < len; sourceIndex++) {
      const source = innerFrom(sources[sourceIndex]);
      let hasValue = false;
      subscriber.add(
        source.subscribe({
          next: (value) => {
            if (!hasValue) {
              hasValue = true;
              emitted++;
            }
            values[sourceIndex] = value;
          },
          error: (err) => { return subscriber.error({ error: err, values }) },
          complete: () => {
            completed++;
            if (completed === len || !hasValue) {
              if (emitted === len) {
                subscriber.next(keys ? keys.reduce((result, key, i) => (((result as any)[key] = values[i]), result), {}) : values);
              }
              subscriber.complete();
            }
          },
        })
      );
    }
  });
}

注意,当发生错误时,您将返回错误以及值:

error: (err) => { return subscriber.error({ error: err, values }) }

我在这里找到了这个解决方案:https://medium.com/better-programming/rxjs-error-handling-with-forkjoin-3d4027df70fc

  @Effect()
  getAllModels$ = this.actions$.pipe(
    ofType<featureActions.GetAllModelsRequest>(featureActions.ActionTypes.GetAllModelsRequest),
    switchMap((action) =>
      forkJoin([
        this.dataService.GetAllModelFromServer().pipe(catchError(() => of({ data: [] }))),
        this.dataService.GetAllModelFromHost().pipe(catchError(() => of({ data: [] }))),
        this.dataService.GetAllModelFromCache().pipe(catchError(() => of({ data: [] }))),
        this.dataService.GetAllModelFromPreference().pipe(catchError(() => of({ data: [] }))),
      ]).pipe(
        map(
          ([server, host, cache, preference]) =>
            new featureActions.GetAllModelsSuccess({
              //...
            })
        ),
        catchError((error: HttpErrorResponse) => {
          return of(new featureActions.GetAllModelsFailed({ error: error.message }));
        })
      )
    )
  );