Search code examples
rxjsrxjs6rxjs-observablesrxjs-pipeable-operators

Polling using expand in rxjs gives an unexpected result


I am trying to setup a poller using expand but the behavior is not what I want

https://stackblitz.com/edit/rxjs-finalize-unsubscribe-6xy2yb?file=index.ts

checkExistence produces a random boolean - With the expand, I expect a recursive delayed call of the same checkExistence function producing random booleans every 5 seconds (after one initial call).

I also expect the stop to kick in 30seconds and stop the polling and 5 seconds later resume the random boolean stream. Any pointers will help.

Instead I get the same boolean value getting printed; also after the start is triggered, it produces batches of booleans together.


Solution

  • This isn't really a single question.

    Assignment/function Invocation

    A simplified example

    The equals operator assigns a value to a variable. Function invocation returns a value

    function inc(n){
      return n + 1
    }
    
    const result = inc(5)
    console.log(result); // 6 
    

    Here, the result holds the number value 6. Not a function that calculated 5 + 1. 5 + 1 only happens once here.

    Now consider this function that randomly increments a number by either 1 or 2

    function bump(n){
      const nu = Math.random();
      return nu < 0.5 ? n + 1 : n + 2 
    }
    
    const result = bump(5);
    console.log(result); // 7
    console.log(result); // 7 
    console.log(result); // 7 
    console.log(result); // 7 
    console.log(result); // 7 
    

    Every time I print the result, it's the same. bump was only called once and only generated a random number once.

    function bump(n){
      const nu = Math.random();
      return nu < 0.5 ? n + 1 : n + 2 
    }
    
    console.log(bump(5)); // 7
    console.log(bump(5)); // 7 
    console.log(bump(5)); // 6 
    console.log(bump(5)); // 7 
    console.log(bump(5)); // 6 
    

    Here, bump is called 5 times, and each time it generated a new random number.

    Fixing checkExistence

    There are two ways.

    • Instead of generating a value once and re-using it. Generate a new boolean in your poll every time you need one.
    function checkExistencePoll(): Observable<boolean> {
      const POLL = 5000;
      return checkExistence().pipe(
        expand(_ =>
          timer(POLL).pipe(
            switchMap(() => checkExistence()),
            takeUntil(stop),
            repeatWhen(() => start)
          )
        )
      );
    }
    
    • Have checkExistence return an observable that generates a new boolean on subscription
    function checkExistence(): Observable<boolean> {
      return defer(() => {
        const nu = Math.random();
        const rand = nu < 0.5;
        console.log(nu, rand);
        return of(rand);
      });
    }
    

    Handling subscriptions

    In your code, expand is generating an observable that subscribes to a subject ( called start) in order to decide when to repeat. Every observable expand creates does this. So each one repeats when start emits. You should expect batches of booleans equal to the number of concurrent observables you've created.