Search code examples
rxjsobservablesettimeout

Recursive function with timeout and check RxJs Observable value polling


I have the recursive function: repeatAlert that is called again if data.answered === null:

.... Edit

      this.repeatAlert(id).subscribe( val => console.log(val));
      console.log('1stCall Alert: ', new Date().getMinutes());

....

  find(id: number): Observable<any> {
        return this.http.get(`${this.resourceUrl}ByAlertId/${id}`
  }

  repeatAlert(id: number) {
    this.find(id).subscribe((data: AlertInt) => {
       if (data.answered === null ) {
          this.sendNotification('Alert ', data.text);
          console.log('Call Alert: ', new Date().getMinutes(), data.id);

          setTimeout(() => {
            if (data.answered === null) {
              this.repeatAlert(id);
            }
          }, data.repeating * 1000 * 60);
       }
    });
  }

When I change the value of data.answered in the database, I can't read with this observable find(id) the change of data.answered. So it keeps calling repeatAlert forever ...

What am I doing wrong?

Extra question: Is it better a loop or recursive function ?


Solution

  • You are doing polling. I suggest something like following:

    find(id: number): Observable<any> {
      return this.http.get(`${this.resourceUrl}ByAlertId/${id}`;
    }
    
    repeatAlert(id: number) {
      // request data from the endpoint and execute necessary code
      const data$ = this.find(id).pipe(
        tap(data => {
          if (data.answered === null) {
            this.sendNotification('Alert ', data.text);
          }
        })
      );
    
      // polling will start if on the first try we don't have data.answered
      const startPolling = (duration: number) => timer(duration, duration).pipe(
        //take(10), // let's say we want to stop after 10 tries
        concatMap(() => data$),
        takeWhile(data => data.answered === null), // when to stop polling
      );
    
      // if data.answered is null on the first try switch to polling otherwise end
      return data$.pipe(
        switchMap(data => data.answered === null ?
          startPolling(data.repeating * 1000 * 60) :
          of(data)
        ),
      );
    }
    

    Also note that I changed your repeatAlert, it's better to return Observable from the method and subscribe yourself to avoid memory leaks. You should subscribe and unsubscribe yourself. Also, I suggest you to use take(10) for example so that polling doesn't continue indefinitely, it's up to you.


    timer(dueTime, period) works like this: It will emit first event after dueTime and continue emitting events after every period.

    Edit takeWhile condition is true and not condition is false