Angular 7 HttpClient ConcatMap 错误处理

Angular 7 HttpClient ConcatMap error handling

我有一些代码可以将大文件分块上传。这是我拥有的:

upload(project: Project): Observable<any> {

    var chunks = this.chunkFiles(project);

    var totalPercent = project.file
        .map(file => Math.ceil(file.size / this.chunkSize))
        .reduce((sum, curr) => sum + curr) * 100;

    var finishedPercent = 0;
    var currentPercent = 0;

    return from(chunks)
        .pipe(
            concatMap(request =>
            {
                return this.http.request(request)
                    .pipe(
                        retryWhen(error =>
                        {
                            return interval(1000)
                                .pipe(
                                    flatMap(count =>
                                    {
                                        if (count == 2)
                                        {
                                            return throwError(error);
                                        }
                                        return of(count);
                                    })
                                );
                        }));
            }),
            map(event =>
            {
                if (event.type === HttpEventType.UploadProgress)
                {
                    var progress = event as HttpProgressEvent;
                    currentPercent = Math.round(100 * progress.loaded / progress.total);
                    return {
                        type: event.type,
                        loaded: finishedPercent + currentPercent,
                        total: totalPercent
                    };
                }
                else if (event instanceof HttpResponse) 
                {
                    finishedPercent += 100;
                    if (finishedPercent == totalPercent)
                    {
                        return event;
                    }
                }
            }),
            filter(response => response !== undefined)
        );
}

目的是它开始上传文件块,一旦其中一个块上传失败,它应该停止并且错误应该传播到调用我的上传函数的代码。该特定代码如下:

onStartUpload = this.actions$
    .ofType(forRoot.START_UPLOAD).pipe(
    switchMap((action: forRoot.StartUpload) =>
        this.uploadService
            .upload(action.project).pipe(
            map((event) => {
                if (event.type === HttpEventType.UploadProgress) {
                    const progress = event as HttpProgressEvent;
                    const percentDone = Math.round(100 * progress.loaded / progress.total);
                    return new forRoot.UploadProgress(percentDone);
                } else if (event instanceof HttpResponse) {
                    return new forRoot.UploadSucceeded();
                } else {
                    console.log(event);
                }
            }),
            filter(a => a !== undefined),
            catchError(error => {
                if (error instanceof HttpErrorResponse && error.status === 400) {
                    const message = this.getMessage(error);
                    return of(new forRoot.UploadFailed(message || "Bad request"));
                } else {
                    return of(new forRoot.UploadFailed("Network error"));
                }
            })))
        );

问题是我只收到一条 "Network error" 消息,而我期待错误状态为 400 的消息(所以我应该收到特定消息或 "Bad request".

我假设我没有正确处理 retryWhen 中的错误,但我尝试了一些不同的想法并且 none 似乎有效。我错过了什么?

编辑以显示工作代码而不重试:

这里是错误消息处理正确的代码,但是没有重试:

return from(chunks)
    .pipe(
        concatMap(request =>
        {
            return this.http.request(request)
        }),
        map(event =>
        {
            if (event.type === HttpEventType.UploadProgress)
            {
                var progress = event as HttpProgressEvent;
                currentPercent = Math.round(100 * progress.loaded / progress.total);
                return {
                    type: event.type,
                    loaded: finishedPercent + currentPercent,
                    total: totalPercent
                };
            }
            else if (event instanceof HttpResponse) 
            {
                finishedPercent += 100;
                if (finishedPercent == totalPercent)
                {
                    return event;
                }
            }
        }),
        filter(response => response !== undefined)
    );

我一直假设我可以在 http.request 调用中添加一些东西来重试指定次数并在重试之间有延迟,然后如果它们都失败了,抛出一个错误就像http.request 确实如此。似乎发生的是它继续经过 concatMap 代码到映射代码。我可以添加 console.log(event) 调用并查看从服务器返回的错误消息。我对 rxjs 还是很陌生,所以也许我不明白,但我期待一旦错误发生它不会执行地图代码。

问题在这里:

retryWhen(error => {     // << error is a stream
  return interval(1000)
    .pipe(
        flatMap(count => {
          if (count == 2){
              return throwError(error); // so here a stream is thrown
          }

          return of(count);
      })
    );
})

error 参数不是单个错误,而是所有发生的错误的 Observable。

要解决此问题,您应该 return 派生自 error 的可观察对象,例如:

retryWhen(errors$ =>
  errors$.pipe(
    delay(1000)
  )
)

有限错误处理的更新示例

retryWhen(errors =>
  errors.pipe(
    // switchMap to add limiting logic
    switchMap((error, index) =>
      index == 3                      // if it is a 4th error
      ? throwError(error)             //  -- we rethrow it upstream
      : of(error).pipe( delay(1000) ) //  -- delay a retry otherwise
    )
  )
)

这里是 retryWhen example with exponential backoff.

还有一篇文章 on error handling in rxjs(附有关于 retryWhen 提前完成的注释)。

希望对您有所帮助