2

I have following code that mimics HTTP Requests polling.

  timeout:Observable<number> = timer(10000);

  startPollingStackblitz(arnId: string) {
    const poll:Observable<BuyingData[]> = of({}).pipe(
        mergeMap(_ => {
          console.log('polling...' + arnId);
          return of([]);
          // return this.service.getData(arnId);
        }),
        takeUntil(this.timeout),
        tap(_ => console.info('---waiting 2 secs to restart polling')),
        delay(2000),
        repeat(),
        tap(_ => console.info('---restarted polling')),
      );

    this.subscription = poll.subscribe((data) => {
      console.log('subscribe...')
      if (data.length > 0) {
        console.log('timeout...');
        console.log(this.timeout);// I want to stop polling immediately before timer will elapse
      }
    });
  }

I want my polling stops sending HTTP Requests (it logs 'polling...' at this demo version) when server responds with data.length > 0. For some reason it continues to send Requests even after 10000ms timeout. How can I do that?

Michael Z
  • 3,883
  • 10
  • 43
  • 57
  • Maybe use `retry` instead of `repeat`? – Mladen Aug 13 '20 at 12:20
  • I am afraid not. Please see Example 2 from here https://www.learnrxjs.io/learn-rxjs/recipes/http-polling – Michael Z Aug 13 '20 at 12:27
  • `timer(10000)` will emit 0 after 10 seconds which is a falsy value. So `takeUntil` won't stop the pipe. You can use an overload of `timer(10000,1)` to make it truthy or use the `timeout` operator instead merge it with a subject that you can fire in the subscription. – Eldar Aug 13 '20 at 12:34
  • @Eldar doesn't matter if the stream under the `takeUntil` emits a falsy value, it's enough that it _emits_. Even a `Subject` would do. – mbojko Aug 13 '20 at 12:38
  • @Eldar thanks for you answer but `timer(10000,1)` did not help. Could you write a code snippet for your another theory with `timeout` operator ? – Michael Z Aug 13 '20 at 12:45

2 Answers2

0

Repeat returns an Observable that will resubscribe to the source stream when the source stream completes, in your case although the source Observable completes (thanks to takeUntil), using repeat would resubcribe to source stream repeatedly

Instead of repeat your can try the following:

const poll :Observable<BuyingData[]> = interval(2000).pipe(
  exhaustMap(() => this.service.getData())
  takeUntil(this.timeout),
  takeWhile(data => data.length > 0),
);
Rafi Henig
  • 5,950
  • 2
  • 16
  • 36
0

Well, as I understand you have two stopping conditions :

  1. After a timeout (10sec)
  2. When a response meets your condition (data.length > 0)

You can achieve this by combining takeUntil, race, and timer operators with a subject as below.

const stopper = new Subject(); // to stop emitting
const poll = of({}).pipe(
  mergeMap(_ =>
    fakeDelayedRequest().pipe(
      catchError(e => {
        console.error(e);
        return of(false);
      })
    )
  ),
  tap(write),
  tap(_ => console.info("---waiting 3 secs to restart polling")),
  delay(3000),
  tap(_ => console.info("---restarted polling")),
  repeat(),
  takeUntil(stopper.pipe(race(timer(10000)))) // this should be the last in the pipe
  // else `repeat` operator will be repeating without a condition.
);

poll.subscribe(_ => {
  const rnd = Math.random();
  if (rnd> 0.3) { // random stop condition
    console.log("closing !",rnd);
    stopper.next(); // emit the stop
  }
});

takeUntil will stop when the target observable emits a value. timer will emit a value after 10 secs. race will emit a value either from stopper or from timer which comes first.

Stackblitz

Eldar
  • 9,781
  • 2
  • 10
  • 35