具有自动递增延迟的rxjs自定义retryWhen策略无法正常工作
我正在尝试创建一个自定义的具有自动递增延迟的rxjs自定义retryWhen策略无法正常工作,rxjs,rxjs6,Rxjs,Rxjs6,我正在尝试创建一个自定义的retryWhen策略,该策略尝试重试N次,中间有X个延迟,之后失败。在某种程度上,这个例子正是我所要寻找的 不幸的是,这段代码有一个问题,我似乎不知道如何解决。 在我的例子中,可观察对象可能会随机失败-您可以尝试2次成功的,然后尝试2次不成功的。一段时间后,订阅将自动完成,因为retrytrys将超过最大值,尽管在实践中没有出现这种情况 为了更好地理解这个问题,我创建了一个 答复将是: Attempt 1: retrying in 1000ms 0 1
retryWhen
策略,该策略尝试重试
N次,中间有X个延迟,之后失败。在某种程度上,这个例子正是我所要寻找的
不幸的是,这段代码有一个问题,我似乎不知道如何解决。
在我的例子中,可观察对象可能会随机失败-您可以尝试2次成功的
,然后尝试2次不成功的
。一段时间后,订阅将自动完成,因为retrytry
s将超过最大值,尽管在实践中没有出现这种情况
为了更好地理解这个问题,我创建了一个
答复将是:
Attempt 1: retrying in 1000ms
0
1
Attempt 2: retrying in 2000ms
Attempt 3: retrying in 3000ms
0
1
We are done!
但事实上应该是这样
Attempt 1: retrying in 1000ms
0
1
Attempt 1: retrying in 1000ms <-- notice counter starts from 1
Attempt 2: retrying in 2000ms
0
1
Attempt 1: retrying in 1000ms <-- notice counter starts from 1
0
1
Attempt 1: retrying in 1000ms <-- notice counter starts from 1
Attempt 2: retrying in 2000ms
0
1
... forever
尝试1:在1000毫秒内重试
0
1.
尝试1:在1000ms内重试我认为文档中给出的示例是针对只发出一次然后完成的可观察对象编写的,例如http get。假设如果您想获得更多数据,那么您将再次订阅,这将重置genericRetryStrategy
中的计数器。但是,如果您现在想将相同的策略应用于一个长时间运行的可观察对象,该对象的流除非给出错误(例如您使用interval()
),否则将无法完成,那么您需要修改generictrystategy()
,以便在需要重置计数器时被告知
这可以通过多种方式实现,我已经给出了一个简单的例子,基于你所说的你正在努力实现的目标。请注意,我还稍微改变了您的逻辑,使其更符合您所说的“两次成功尝试,然后两次失败尝试”。但重要的是修改抛出到genericretstrategy()
中的错误对象,以传达失败尝试的当前计数,以便它能够做出适当的反应
以下是为完整起见复制的代码:
import { timer, interval, Observable, throwError } from 'rxjs';
import { map, switchMap, tap, retryWhen, delayWhen, mergeMap, shareReplay, finalize, catchError } from 'rxjs/operators';
console.clear();
interface Err {
status?: number;
msg?: string;
int: number;
}
export const genericRetryStrategy = ({
maxRetryAttempts = 3,
scalingDuration = 1000,
excludedStatusCodes = []
}: {
maxRetryAttempts?: number,
scalingDuration?: number,
excludedStatusCodes?: number[]
} = {}) => (attempts: Observable<any>) => {
return attempts.pipe(
mergeMap((error: Err) => {
// i here does not reset and continues to increment?
const retryAttempt = error.int;
// if maximum number of retries have been met
// or response is a status code we don't wish to retry, throw error
if (
retryAttempt > maxRetryAttempts ||
excludedStatusCodes.find(e => e === error.status)
) {
return throwError(error);
}
console.log(
`Attempt ${retryAttempt}: retrying in ${retryAttempt *
scalingDuration}ms`
);
// retry after 1s, 2s, etc...
return timer(retryAttempt * scalingDuration);
}),
finalize(() => console.log('We are done!'))
);
};
let int = 0;
let err: Err = {int: 0};
//emit value every 1s
interval(1000).pipe(
map((val) => {
if (val > 1) {
//error will be picked up by retryWhen
int++;
err.msg = "equals 1";
err.int = int;
throw err;
}
if (val === 0 && int === 1) {
err.msg = "greater than 2";
err.int = 2;
int=0;
throw err;
}
return val;
}),
retryWhen(genericRetryStrategy({
maxRetryAttempts: 3,
scalingDuration: 1000,
excludedStatusCodes: [],
}))
).subscribe(val => {
console.log(val)
});
从'rxjs'导入{timer,interval,Observable,throwerr};
从“rxjs/operators”导入{map、switchMap、tap、retryWhen、delayWhen、mergeMap、shareReplay、finalize、catchError};
console.clear();
接口错误{
状态?:编号;
msg?:字符串;
int:数字;
}
导出常量一般系统策略=({
maxRetryAttempts=3,
缩放持续时间=1000,
excludedStatusCodes=[]
}: {
maxRetryAttempts?:数字,
缩放持续时间?:数字,
excludedStatusCodes?:编号[]
}={}=>(尝试次数:可观察)=>{
回流管(
合并映射((错误:Err)=>{
//我在这里不重置并继续递增?
const retrytry=error.int;
//如果已达到最大重试次数
//或者响应是我们不希望重试的状态代码,抛出错误
如果(
重试次数>最大重试次数||
excludedStatusCodes.find(e=>e==error.status)
) {
返回投掷器(错误);
}
console.log(
`尝试${RetryAttenting}:在${RetryAttenting}中重试*
缩放持续时间}ms`
);
//在1s、2s等之后重试。。。
返回计时器(重试尝试*缩放持续时间);
}),
finalize(()=>console.log('wedone!'))
);
};
设int=0;
设err:err={int:0};
//每1s发射一次值
间隔(1000)。管道(
地图((val)=>{
如果(val>1){
//错误将由retryWhen拾取
int++;
err.msg=“等于1”;
err.int=int;
犯错误;
}
如果(val==0&&int==1){
err.msg=“大于2”;
err.int=2;
int=0;
犯错误;
}
返回val;
}),
retryWhen(一般系统策略)({
最大尝试次数:3次,
缩放持续时间:1000,
排除状态代码:[],
}))
).订阅(val=>{
console.log(val)
});
对我来说,这仍然是非常必要的,但如果不深入了解您试图解决的问题,我目前想不出更具声明性的方法…谢谢您的回复!我可能无意中用if/else
s误导了你。目的是模拟场景。本质上,策略
应该重新连接(延迟),并在连续5次失败时放弃。但是,由于mergeMap(error,i)
中的i
变量在成功尝试后未重置为0
,因此可观察对象的完成不正确。因此,如果您有4次连续故障,一次成功连接,然后是一次故障-可观察的完成
s,而它应该再继续4次,直到它放弃为止。i
变量未重置是我在回答的第一段中得到的-如前所述,它只会“重置为零”一个新的订阅。然而,在您的示例中,您使用的是一个长时间运行的Observable,它发出多个值。对于您的实际用例(不是这个人为的例子),您的实际源在一次发射(例如http get)后是否完成了观测?它没有-它是一个EventSource
我实际上通过。。。版本似乎为我解决了这个问题。好的,很高兴你把它解决了。:)