【问题标题】:Poll a service until I get a response or time out after certain time period轮询服务,直到我得到响应或在特定时间段后超时
【发布时间】:2020-12-31 21:22:41
【问题描述】:

要求 调用服务。

如果该服务返回数据,请将数据设置为变量。功能超过

如果该服务返回 data = null,则每 20 秒重复调用该服务,直到它返回 data=“列表或对象”或调用该服务 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 分钟。那是行不通的。

这段代码没必要用,我要达到要求。可以有不同的方法。

【问题讨论】:

  • @user630209 查看我的更新
  • 如果响应数据为空,服务需要再次调用。这不起作用。
  • 状态:429,状态文本:“请求过多”
  • 发生错误后可观察到的停止

标签: angular typescript rxjs angular8


【解决方案1】:

只需使用find 运算符(查找通过某些测试的第一个值并发出Docs),因此getUrlById 返回的Observable 在response.result 定义后立即完成,如下所示:

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 => { })

这是LIVE DEMO

可以选择使用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 => { })

【讨论】:

  • find(x => x?.result) - 这一行抛出错误。 'Object'.ts(2339) 类型上不存在属性'result'
  • find((x: any) => x?.result) 运行 ng serve 时出错。错误 TS1109:预期表达式。 src/app/register.component.ts(420,31): 错误 TS1005: ':' 预期。 i 「wdm」:编译失败。
  • 看我的更新,其实getUrlById应该返回一个实际类型而不是返回类型object
  • find((x: any) => x?.result) 。这是来自更新。为什么在这一行出错。 ':' 预期编译失败。
  • 我怀疑链接运算符在 Angular 8 中不起作用(不确定),请查看我的更新
【解决方案2】:

首先,我想说timeout 运算符在这种情况下是多余的。如果 source600000ms 内没有发出,它应该会抛出一个错误,但这永远不会发生,因为源会在每个2000ms 发出一次。

考虑到这一点,这是我的方法:

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)),
)

它之前不起作用可能是因为在填充this.url 之前到达takeWhile(() =&gt; this.url),这意味着当调用takeWhile 的回调函数时,this.urlundefined(任何虚假值),所以整个流就完成了。

【讨论】:

  • 我可以在某个时候将值 res["result"] 保存到 this.url 变量中吗?
  • @user630209 是的,只需在filter() 之后添加tap(res =&gt; this.url = res["result"])
  • 为什么在find 可用时使用filer + first
  • @RafiHenig 感谢您的指出,我想说它们可以互换使用,尽管它们中的每一个都可以扩展到其他用例。例如,对于find,您可以选择仅返回对值调用的 cb 返回 true 的 indexfirst 可以有默认值,而 find 没有。
  • " timeout operator is redundant in this case. It is supposed to throw an error if the source does not emit in 600000ms, but this will never happen, since the source will emit every 2000ms." 这不是真的,因为 filter/find 会过滤掉不需要的值。
【解决方案3】:

您是否尝试过使用 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!`)
});

【讨论】:

    【解决方案4】:

    每当轮询出现时,通常要使用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),
    );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-09
      • 1970-01-01
      • 2015-03-08
      • 1970-01-01
      • 2015-05-01
      • 1970-01-01
      • 2020-02-22
      • 1970-01-01
      相关资源
      最近更新 更多