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
提前完成的注释)。
希望对您有所帮助
我有一些代码可以将大文件分块上传。这是我拥有的:
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
提前完成的注释)。
希望对您有所帮助