Angular 7 HttpClient ConcatMap错误处理

时间:2019-03-29 19:27:59

标签: angular rxjs angular-httpclient

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

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"));
                }
            })))
        );

问题是我只收到“网络错误”消息,而我期望的是错误状态为400的消息(因此,我应该收到特定消息或“错误请求”。

我假设我没有在retryWhen内正确处理错误,但是我尝试了一些不同的想法,但似乎没有一个可行。我想念什么?

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

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

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还是很陌生,所以也许我听不懂,但是我期望一旦发生错误,它就不会执行映射代码。

1 个答案:

答案 0 :(得分:0)

问题在这里:

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参数不是一个错误,而是所有发生的错误的可观察值。

要解决此问题,您应该返回源自error的observable,例如:

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提前完成的注释)。

希望这会有所帮助