Хвостовая рекурсивная наблюдаемая функция возврата с помощью Rxjs

#angular #rxjs #angular-observable

Вопрос:

Предположим, у меня есть служба с методом, который отправляет URL-адрес до тех пор, пока не будет выполнено определенное условие. После этого он возвращает результат. Все это должно быть построено с помощью наблюдаемых объектов (чтобы их можно было легко отменить извне, поэтому никакой рекурсии с обещаниями или тому подобным).

Я попробовал это, и я ожидал, что буду звонить каждую секунду, пока не будет выполнено условие, а затем возвращать результат.

 getData(): Observable<MyData> {
  return this.apiService.get('/url').pipe(
    map((res) => {
      if (...certain condition concerning 'res') {
        return res;
      }
      return timer(1000).pipe(
        map(() => this.getData())
      );
    })
  );
}
 

Если я вызову его таким образом, хотя apiService вызывается только один раз, и результат в методе подписки решается сразу и является другим наблюдаемым. Что-то определенно не так, что я делаю, но не могу понять, что именно, вероятно, при возврате отложенного рекурсивного вызова.

 getData().subscribe(res => {
  // res is an Observable, I expected an MyData after a delay of at least 1 second
  // as the condition is guaranteed to fail the first time (for testing purposes)
})
 

Ответ №1:

С помощью рекурсии RxJS часто подразумевается использование expand оператора.

В вашем случае вы могли бы попробовать что-то вроде этого

 // condition is a function that returns true if a certain condition on res is met, otherwise false
const condition = (res) => {
   if (...certain condition concerning 'res') {
        return true;
      } else {
        return false
      }
};

function getData() {
  return this.apiService.get('/url').pipe(
    // If the condition is met the Observable stream is completed with no value notified
   // if instead the condition in not met, then the API call is performed again
    expand((res) => {
      return condition(res) ? EMPTY : this.apiService.get('/url').pipe(delay(1000));
    }),
    // this filter condition is optional and its use is to avoid notifying values
    // until the expected one is received
    filter(condition)
  );
}
 

Вот штекблитц, который имитирует эту логику.