在RXJS中,如何使用retryWhen强制重新执行使用bindNodeCallback创建的可观察对象?
我有一个在RXJS中,如何使用retryWhen强制重新执行使用bindNodeCallback创建的可观察对象?,rxjs,rxjs6,Rxjs,Rxjs6,我有一个Subject,它接收对象,然后调用我用bindNodeCallback包装的函数。如果由于错误或结果为false而失败,我想使用retryWhen重试该函数的执行 我尝试了几种不同的方法,但没有成功地使绑定函数再次启动 我有一本书 函数fakeSend( 任务:字符串, cb:(err:Error | null,结果?:boolean)=>void ) { console.log(“fakesend”,任务); 设置超时(()=>{ const hasrerror=Math.rando
Subject
,它接收对象,然后调用我用bindNodeCallback
包装的函数。如果由于错误或结果为false而失败,我想使用retryWhen
重试该函数的执行
我尝试了几种不同的方法,但没有成功地使绑定函数再次启动
我有一本书
函数fakeSend(
任务:字符串,
cb:(err:Error | null,结果?:boolean)=>void
) {
console.log(“fakesend”,任务);
设置超时(()=>{
const hasrerror=Math.random()<0.5;
const res=Math.random()<0.5;
log(hasError?“hasError”:`用${res}响应');
if(hasrerror){
返回cb(新错误(“错误”));
}
返回cb(null,res);
}, 100);
}
const boundSend=bindNodeCallback(fakeSend);
常量主题=新主题();
subject.subscribe(
(任务)=>{
边界发送(任务)
.烟斗(
轻触((状态)=>{
如果(!状态){
抛出新错误(“未发送”);
}
返回状态;
}),
retryWhen((错误)=>
烟斗(
延迟(1000),
轻触((错误)=>console.log)
)
)
)
.订阅({
下一步:console.log,
错误:console.error,
完成:()=>{
console.log(“完成”,任务);
}
});
},
(错误)=>{
console.log(“主题订阅错误”);
}
);
主题。下一步(“测试1”);
主题。下一步(“测试2”);
主题。下一步(“测试3”);
主题。下一步(“测试4”);
谢谢
const{Subject,bindNodeCallback}=rxjs;
const{tap,retryWhen,delay}=rxjs.operators;
功能伪造(
任务
cb
) {
console.log(“fakesend”,任务);
设置超时(()=>{
const hasrerror=Math.random()<0.5;
const res=Math.random()<0.5;
log(hasError?“hasError”:`用${res}响应');
if(hasrerror){
返回cb(新错误(“错误”));
}
返回cb(null,res);
}, 100);
}
const boundSend=bindNodeCallback(fakeSend);
常量主题=新主题();
subject.subscribe(
(任务)=>{
边界发送(任务)
.烟斗(
轻触((状态)=>{
console.log('tap');
如果(!状态){
抛出新错误(“未发送”);
}
返回状态;
}),
retryWhen((错误)=>
烟斗(
延迟(1000),
轻触((错误)=>console.log)
)
)
)
.订阅({
下一步:console.log,
错误:console.error,
完成:()=>{
console.log(“完成”,任务);
}
});
},
(错误)=>{
console.log(“主题订阅错误”);
}
);
主题。下一步(“测试1”);
主题。下一步(“测试2”);
主题。下一步(“测试3”);
主题。下一步(“测试4”)代码>
如果希望在出现错误时通过retryWhen
操作符重试基于回调的函数,我认为您可以尝试使用新的Observable
构造函数,而不是bindNodeCallback
。下面是一些细节
首先,您可以使用如下的新的Observable
构造函数从基于回调的函数中创建一个Observable
function newSend(task: string) {
return new Observable(
(subscriber: Subscriber<string>): TeardownLogic => {
fakeSend(task, (err: Error | null, result?: string) => {
if (err) {
subscriber.error(err);
} else {
subscriber.next(result);
subscriber.complete();
}
});
}
);
}
subject
.pipe(
mergeMap((task) => {
return newSend(task);
})
)
.subscribe(
(data) => console.log("Notification", data),
(error) => {
console.log("Error in final subscription", error);
},
() => console.log("DONE")
);
subject.next("test1");
subject.next("test2");
subject.next("test3");
subject.next("test4");
subject.complete(); // added just to show that the complete function of the subscriber can be invoked
subject
.pipe(
mergeMap((task) => {
return newSend(task).pipe(
retryWhen((errs) =>
errs.pipe(
delay(1000),
tap((err) => console.log("Retry", err.message))
)
)
);
})
)
如果我们执行上述代码,我们会看到主题上的订阅将所有成功调用记录到fakeSend
,直到遇到错误,此时它会记录错误并终止(请参阅)
然后,我们可以添加retryWhen
操作符,以便在fakeSend
回调出现错误时实际重试,如下所示
function newSend(task: string) {
return new Observable(
(subscriber: Subscriber<string>): TeardownLogic => {
fakeSend(task, (err: Error | null, result?: string) => {
if (err) {
subscriber.error(err);
} else {
subscriber.next(result);
subscriber.complete();
}
});
}
);
}
subject
.pipe(
mergeMap((task) => {
return newSend(task);
})
)
.subscribe(
(data) => console.log("Notification", data),
(error) => {
console.log("Error in final subscription", error);
},
() => console.log("DONE")
);
subject.next("test1");
subject.next("test2");
subject.next("test3");
subject.next("test4");
subject.complete(); // added just to show that the complete function of the subscriber can be invoked
subject
.pipe(
mergeMap((task) => {
return newSend(task).pipe(
retryWhen((errs) =>
errs.pipe(
delay(1000),
tap((err) => console.log("Retry", err.message))
)
)
);
})
)
在这一点上,我们的逻辑是完整的。每当调用带有错误的fakeSend
回调时,retryWhen
操作符将确保使用相同参数执行对fakeSend
的新调用。完整的代码可以在中看到
为什么它不能与bindNodeCallback一起使用
原因是,bindNodeCallback
的实现在内部使用了一个AsyncSubject
,它缓存最后一个结果,并在后续订阅中重放它。因此,如果基于回调的函数出错,并且retryWhen
操作符再次订阅,那么订阅将再次生成相同的错误,从而启动无限循环。如果您将newSend(task)
替换为boundSend(task)
(第46行和第47行)可以看出这一点。我使您的脚本可以在本地代码段中运行。如果有解决方案,则可以更轻松地复制代码段以回答问题you@AdrianBrand太棒了,谢谢你!最好将问题的关联代码存储在StackOverflow上,而不是外部站点上。希望我们能很快得到TypeScript代码段的支持。提示StackOverflow开发者!也许即使有npm支持,我们也不必依赖CDN。你不应该在rxjs中看到嵌套订阅。我吃了一顿大醉的午餐,现在还没来得及用rxjs思考,但如果没有其他人插话的话,我可能明天再看。谢谢你如此透彻的解释。你真是太棒了。这是一个有趣的问题,也帮助了我