在 Angular 应用中重试请求多次
Retry a request for a number of times in Angular app
我有这种情况需要发出get请求。我确实知道如何使用 Angular 的 http 客户端,但我需要在请求失败时自动重试多次,然后再放弃并抛出错误,而且我还需要延迟在每次重试之间。
例如,我需要我的请求,如果失败,总共重试5次,并且每次重试之间有10秒的延迟。
有没有一种简单明了的方法来做这样的事情?
在 angular rxj 中有一个名为“retry(number)”的方法,您可以在 (https://angular.io/guide/http):
上阅读更多内容
getConfig() {
return this.http.get<Config>(this.configUrl).pipe(
retry(3), // retry a failed request up to 3 times
catchError(this.handleError) // then handle the error
);}
我的 angular 应用程序也有同样的需求,因此我创建了一个名为 retryWithBackoff 的管道运算符。它使用指数退避,因此重试之间的时间如下:
delayMs * Math.pow(2, retries)
可以很简单的使用:
getDiscussionList(): Observable<DiscussionListModel[]> | Observable<never> {
return this.httpClient
.get<DiscussionListModel[]>('/api/discussions').pipe(
retryWithBackoff()
);
这里是运算符:
export function retryWithBackoff<T>(
delayMs: number = 1000,
maxRetries: number = 5,
maxTime: number = 12000
): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => {
const currentMs = new Date().getTime();
return source.pipe(
timeout(maxTime),
retryWhen(errors => {
let retries = 0;
return errors.pipe(
mergeMap(next => {
// If we caught TimeoutError we must rethrow because retryWhen would retry
// if request failed due to timeout.
// The timeout(maxTime) isn't reached because a new request is sent
// therefore we have to compare against currentMs
if ((next instanceof TimeoutError)
|| (new Date()).getTime() >= currentMs + maxTime) {
return throwError(new HttpTimeoutError());
}
retries++;
if (retries >= maxRetries) {
return throwError(new HttpMaxRetriesError());
}
return timer(delayMs * Math.pow(2, retries));
}),
);
})
);
};
}
无论尝试了多少次,它都会在 12 秒后超时。它可以抛出的两个异常只是一个空的自定义异常 class:
export class HttpEstablishError {}
export class HttpMaxRetriesError extends HttpEstablishError {}
export class HttpTimeoutError extends HttpEstablishError {}
我还有一些不完整的茉莉花测试:
describe('Utilities', () => {
describe('retryWithBackoff should', () => {
it('return success observable after failing max-1 times', (done) => {
const source = (count, maxRetries) => {
return defer(() => {
if (count <= maxRetries) {
count++;
return throwError(true);
} else {
return of(true);
}
});
};
source(1, 5 - 1).pipe(retryWithBackoff(1, 5)).subscribe(
(value) => {
expect(value).toBe(true);
},
() => {
fail();
done();
},
() => {
done();
}
);
});
it('raise HttpTimeoutError if maxTime is reached', (done) => {
const maxTime = 1000;
const source = new Subject<any>();
source.pipe(retryWithBackoff(1000, 5, maxTime)).subscribe(
() => {
fail('should not happen');
},
(err) => {
expect(err).toBeInstanceOf(HttpTimeoutError);
done();
}
);
});
});
it('raise HttpMaxRetriesError is maxRetries is reached', (done) => {
const source = (count, maxRetries) => {
return defer(() => {
if (count <= maxRetries) {
count++;
return throwError(true);
} else {
return of(true);
}
});
};
source(1, 5 + 1).pipe(retryWithBackoff(1, 5)).subscribe(
() => {
},
(err) => {
expect(err).toBeInstanceOf(HttpMaxRetriesError);
done();
},
);
});
});
我有这种情况需要发出get请求。我确实知道如何使用 Angular 的 http 客户端,但我需要在请求失败时自动重试多次,然后再放弃并抛出错误,而且我还需要延迟在每次重试之间。
例如,我需要我的请求,如果失败,总共重试5次,并且每次重试之间有10秒的延迟。
有没有一种简单明了的方法来做这样的事情?
在 angular rxj 中有一个名为“retry(number)”的方法,您可以在 (https://angular.io/guide/http):
上阅读更多内容 getConfig() {
return this.http.get<Config>(this.configUrl).pipe(
retry(3), // retry a failed request up to 3 times
catchError(this.handleError) // then handle the error
);}
我的 angular 应用程序也有同样的需求,因此我创建了一个名为 retryWithBackoff 的管道运算符。它使用指数退避,因此重试之间的时间如下:
delayMs * Math.pow(2, retries)
可以很简单的使用:
getDiscussionList(): Observable<DiscussionListModel[]> | Observable<never> {
return this.httpClient
.get<DiscussionListModel[]>('/api/discussions').pipe(
retryWithBackoff()
);
这里是运算符:
export function retryWithBackoff<T>(
delayMs: number = 1000,
maxRetries: number = 5,
maxTime: number = 12000
): MonoTypeOperatorFunction<T> {
return (source: Observable<T>) => {
const currentMs = new Date().getTime();
return source.pipe(
timeout(maxTime),
retryWhen(errors => {
let retries = 0;
return errors.pipe(
mergeMap(next => {
// If we caught TimeoutError we must rethrow because retryWhen would retry
// if request failed due to timeout.
// The timeout(maxTime) isn't reached because a new request is sent
// therefore we have to compare against currentMs
if ((next instanceof TimeoutError)
|| (new Date()).getTime() >= currentMs + maxTime) {
return throwError(new HttpTimeoutError());
}
retries++;
if (retries >= maxRetries) {
return throwError(new HttpMaxRetriesError());
}
return timer(delayMs * Math.pow(2, retries));
}),
);
})
);
};
}
无论尝试了多少次,它都会在 12 秒后超时。它可以抛出的两个异常只是一个空的自定义异常 class:
export class HttpEstablishError {}
export class HttpMaxRetriesError extends HttpEstablishError {}
export class HttpTimeoutError extends HttpEstablishError {}
我还有一些不完整的茉莉花测试:
describe('Utilities', () => {
describe('retryWithBackoff should', () => {
it('return success observable after failing max-1 times', (done) => {
const source = (count, maxRetries) => {
return defer(() => {
if (count <= maxRetries) {
count++;
return throwError(true);
} else {
return of(true);
}
});
};
source(1, 5 - 1).pipe(retryWithBackoff(1, 5)).subscribe(
(value) => {
expect(value).toBe(true);
},
() => {
fail();
done();
},
() => {
done();
}
);
});
it('raise HttpTimeoutError if maxTime is reached', (done) => {
const maxTime = 1000;
const source = new Subject<any>();
source.pipe(retryWithBackoff(1000, 5, maxTime)).subscribe(
() => {
fail('should not happen');
},
(err) => {
expect(err).toBeInstanceOf(HttpTimeoutError);
done();
}
);
});
});
it('raise HttpMaxRetriesError is maxRetries is reached', (done) => {
const source = (count, maxRetries) => {
return defer(() => {
if (count <= maxRetries) {
count++;
return throwError(true);
} else {
return of(true);
}
});
};
source(1, 5 + 1).pipe(retryWithBackoff(1, 5)).subscribe(
() => {
},
(err) => {
expect(err).toBeInstanceOf(HttpMaxRetriesError);
done();
},
);
});
});