我有一些代码可以以较小的块上传较大的文件。这是我所拥有的:
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还是很陌生,所以也许我听不懂,但是我期望一旦发生错误,它就不会执行映射代码。
答案 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
提前完成的注释)。
希望这会有所帮助