
[英]How do I stop Observable.Timer() or Observable.Interval() automatically after certain period of time
[英]Poll a service until I get a response or time out after certain time period
要求调用服务。
如果该服务返回数据,请将数据设置为变量。 功能结束
如果该服务返回 data = null,则每 20 秒重复调用一次该服务,直到它返回 data="a list or an object" 或调用该服务 2 分钟并停止。
我尝试过的需要每 20 秒轮询一次该服务 getUrlById(id) ,直到在 this.url 中得到响应或在 6 分钟后超时。
尝试了以下未按预期工作的解决方案。
pollUrl(id) {
interval(2000).pipe(
timeout(600000),
takeWhile(() => this.url)
).subscribe(i => {
this.service.getUrlById(id).subscribe(res =>{
if(res && res["result"]){
this.url = res["result"];
}
})
})
}
来自评论我尝试过的
在这里调用了虚拟服务demo 。
这里虚拟服务返回 data = null。 所以根据要求,我需要每 20 秒到 2 分钟调用一次服务。 那是行不通的。
没有必要使用此代码,我想达到要求。 可以有不同的做法。
只需使用find
运算符(查找通过一些测试并发出该Docs的第一个值),这样getUrlById
返回的 Observable 就会在定义response.result
getUrlById
完成,如下所示:
interval(2000)
.pipe(
exhaustMap(() => this.service.getUrlById(id)), // <== switch subscription to `getUrlById`
find(x => x && x.result && x.result.length > 0),
timeout(6 * 60 * 1000)
)
.subscribe(i => { })
这是现场演示
可选地使用expand以便只要当前尚未完成就不会发送进一步的请求。
const api$ = this.service.getUrlById(id);
api$.pipe(
expand(x => x && x.result && x.result.length > 0 ? EMPTY : api$.pipe(delay(2000))),
last(), // get the result
timeout(6 * 60 * 1000)
)
.subscribe(i => { })
您是否尝试过使用 RetryWhen 运算符?
类似于:(链接: https : //stackblitz.com/edit/zhck3h?file=index.ts )
import { interval, of, throwError, zip, from } from 'rxjs';
import { mergeMap, retry, map, retryWhen, delay, take, tap } from 'rxjs/operators';
const randomCall = () => {
const rand = Math.random() * 100;
if (rand > 50) {
console.log('has failed')
throw 'error'
} else {
console.log('has succeed')
return 'success';
};
}
const source = of(1).pipe(
map(() => randomCall()),
take(1)
);
let retries = 5;
const example = source.pipe(
retryWhen(errors => errors.pipe(
delay(1 * 1000),
mergeMap(error => retries-- > 0 ? of(error) : throwError('error'))
))
)
const subscribe = example.subscribe({
next: val => console.log('success: ' + val),
error: val => console.log(`retry exceeded!`)
});
首先,我会说timeout
运算符在这种情况下是多余的。 如果源在600000
毫秒内没有发射,它应该抛出一个错误,但这永远不会发生,因为源将每2000
毫秒发射一次。
考虑到这一点,这是我的方法:
interval(2000).pipe(
switchMap(id => this.service.getUrlById(id)),
filter(res && res["result"]),
// if we got a response -> stop polling
first(),
// if we didn't get a response in `600000`ms -> stop polling
takeUntil(timer(600000)),
)
之前它没有工作可能是因为takeWhile(() => this.url)
在this.url
被填充之前this.url
,这意味着当takeWhile
的回调函数被调用时, this.url
是undefined
(任何假值),所以整个流将完成。
每当轮询出现时,通常要走的路是expand
运算符,一方面,因为当请求花费的时间超过您的轮询周期并且您最终收到两个同时请求时,它会排除这种情况。 就像是
of(undefined).pipe(
expand((result) =>
result === undefined
? this.service.getUrlById(id).pipe(
map((data) => data?.result),
switchMap((result) =>
result !== undefined
? of(result)
: of(undefined).pipe(delay(20000)),
),
)
: EMPTY,
),
filter((result) => result !== undefined),
timeout(600000),
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.