延迟n秒重试轮询服务

时间:2018-08-15 19:09:02

标签: javascript angular rxjs reactive-programming rxjs6

private pollSubscriptions: Subscription;
private defaultPollTime: number = 2000;
constructor(
    private http: HttpClient,
) {
    this.pollSubscriptions = new Subscription();
}

pollRequest<T>(
    url: string,
    updateStatus: any,
    pollWhileCondition: Function,
    onPollingSuccessCallback?: Function,
    timer = this.defaultPollTime
) {
    this.pollSubscriptions.add(timer(0, 2000).pipe(
        switchMap(() => this.http.get<T>(url).pipe(
            catchError((error: any) => empty()))),
        tap(updateStatus),
        takeWhile(data => pollWhileCondition(data)))
        .subscribe());
}

ngOnDestroy(): void {
    this.pollSubscriptions.unsubscribe();
}

我能够同时轮询多个URL。但是如何增强当前功能,以便满足以下要求:

  1. 如果被轮询的网址失败了,那么我们如何以3(n)秒的延迟重试该轮询网址3次?
  2. 如何在要查询的网址上添加不同的运算符?

仍然没有解决方案

预先感谢

3 个答案:

答案 0 :(得分:2)

希望这会有所帮助。

几件事:

  1. 如果您希望所有民意调查都在同一时间进行,则您可能希望使用共享计时器进行民意调查。因此,我在下面添加了一个pollWhen属性。
  2. 您正在寻找retryWhen,这是一个很难理解的运算符,但基本上它的工作原理是这样的:当您订阅时,它会以可观察到的潜在错误调用函数,当错误发生时发出时,您应该将其擦洗为“下一个”值以重试,立即完成操作(例如为空)以安静地完成操作,或者通过错误操作(例如throwError)将其杀死,并观察到错误。
class Component() {
  /** ticks every 10 seconds */
  pollWhen = timer(0, 10000)
    .pipe(share());

  private pollSubscriptions: Subscription;

  constructor(
      private http: HttpClient,
  ) {
      this.pollSubscriptions = new Subscription();
  }

  pollRequest<T>(
      url: string,
      updateStatus: any,
      pollWhileCondition: Function,
      onPollingSuccessCallback?: Function,
      timer = this.defaultPollTime
  ) {
      this.pollSubscriptions.add(this.pollWhen.pipe(
      switchMap(() =>
        this.http.get<T>(url).pipe(
          // Setup retries
          retryWhen(
            errors => errors.switchMap(
              // if more than 3 retries,
              // stop retrying quietly
              (_, i) => i < 3
                ? timer(1000)
                : EMPTY
            )
          )
        )
      ),
      tap(updateStatus),
      takeWhile(pollWhileCondition)
    ).subscribe());
  }

  ngOnDestroy(): void {
    this.pollSubscriptions.unsubscribe();
  }
}

答案 1 :(得分:0)

要回答第一个问题,可以使用retryWhen运算符并替换catch。 对于2个问题,方法需要重新编写一点,您可以将pollRequest更改为subject()来存储并向流发送不同的URL进行处理。

    var pollUrl = new rxjs.Subject()
const updateStatus = () => true
const pollWhileCondition = () => true
const http = url => {
  console.log('http call...',url)
  return rxjs.timer(1000).pipe(
  rxjs.operators.tap(()=>{
    throw "http call error"
  })
  )
}
const distinctUrl = pollUrl.pipe(rxjs.operators.distinct())

distinctUrl.pipe(
  rxjs.operators.mergeMap(url =>  {
    return rxjs.timer(0, 2000).pipe(rxjs.operators.map(() => url))
  }),
  rxjs.operators.tap(()=>console.log('xxx')),
  rxjs.operators.mergeMap(url => http(url).pipe(
  rxjs.operators.retry(3),
   )),
    rxjs.operators.catchError(()=>rxjs.empty()),
    rxjs.operators.repeat()
).subscribe(()=>{},err=>{
console.warn(err)
},()=>console.log('comple'))

pollUrl.next('http://google.com')
setTimeout(()=> pollUrl.next('http://twitter.com') ,7000)

http://jsfiddle.net/cy0nbs3x/1535/

答案 2 :(得分:0)

这不是完整的解决方案,但是在这里,我能够实现3次重试3次(延迟3秒)。我仍在寻找如何区分活动投票URL的方法。非常感谢您的帮助。

import { timer as observableTimer, Subscription, interval, of, concat, Observable } from 'rxjs';
import { takeWhile, tap, take, switchMap, repeat, retryWhen, scan, mapTo, expand, exhaustMap } from 'rxjs/operators';

@Injectable()
export class ReportPollingService {

    private pollSubscriptions: Subscription;
    constructor(private http: HttpClient){}

    pollRequest<T>(url: string, updateStatus: any, pollWhileCondition: Function){
     if (this.pollSubscriptions.closed) {
       this.pollSubscriptions = new Subscription();// re-open polling
     }
     const request$ = this.http.get<T>(url);
     const firstRequest$ = request$;
     const polling$ = interval(options.interval).pipe(
         take(1),
         exhaustMap(() => request$),
         repeat()
     );
     this.pollSubscriptions.add(concat(firstRequest$, polling$).pipe(
         retryWhen(errors$ => {
             return errors$.pipe(
                  scan(
                      ({ errorCount, error }, err) => {
                          return { errorCount: errorCount + 1, error: err };
                      },
                      { errorCount: 0, error: null }
                  ),
                  switchMap(({ errorCount, error }) => {
                      if (errorCount >= 3) {
                           throw error;
                      }
                      return observableTimer(3000, null);
                  })
              );
         }),
     ).pipe(tap(updateStatus), takeWhile(data => pollWhileCondition(data))).subscribe());
   }

   stopPolling(): void {
       this.pollSubscriptions.unsubscribe();
   }