Есть ли способ запустить таймер Rx js по требованию в дополнение к заданным n миллисекундам? - PullRequest
1 голос
/ 18 февраля 2020

Я создал приложение, которое, помимо прочего, показывает оценки определенного продукта. Я хочу обновлять эти оценки каждые 5 секунд, но только когда страница отображается. Если пользователь находится на другой странице, обновление должно прекратиться.

Я создал функцию опроса, используя таймер с rx js в моем ScoresService. Когда ScoresComponent (который показывает оценки) загружен (ngOnInit), он подписывается на scores$. Когда ScoreComponent уничтожен (ngOnDestroy), он отписывается от scores$.


@Injectable({
  providedIn: 'root'
})
export class ScoresService {

  product: Product;
  scores$: Observable<Score[]>

  constructor(
    private http: HttpClient,
    private mainService: MainService
  ) {
    // set a timer for every 5 seconds, starting now, to get scores
    this.scores$ = timer(0, 5000).pipe(
      switchMap(() => this.getScores())
    );


    // when the product is set or changed, update this.product
    this.mainService.product$.subscribe((product) => {
       this.product = product;
       // fire the timer again?
    }
  }

  getScores(): Observable<Score[]> {
    const params = {
      order: 'best',
      from: moment().startOf('week').format('YYYY-MM-DD HH:mm:ss'),
      till: moment().endOf('week').format('YYYY-MM-DD HH:mm:ss'),
      limit: '100',
      product: this.product.id
    };

    return this.http.get<Score[]>(`${api_url}`', { params });
  }

}

Так что таймер работает, когда продукт установлен, конечно. Он получает новые результаты только тогда, когда ScoreComponent подписывается на scores$. Но проблема возникает при смене продукта, потому что получение новых результатов занимает от 0 до 5 секунд. Есть ли способ срабатывания таймера по требованию, например, при смене товара. Если есть другие способы добиться этого, я бы услышал об этом. Единственное, что следует отметить, это то, что только когда кто-то подписан на scores$, должен проводиться опрос. Я не хочу бесконечного опроса конечной точки, если никому не нужны новые результаты.

1 Ответ

2 голосов
/ 18 февраля 2020

Вы можете комбинировать наблюдаемые из продукта и таймера. Это будет запускать getScores каждый раз, когда появляется новый продукт, и запускать новый таймер каждые 5 секунд для получения баллов.

export class ScoresService {
  product: Product;
  scores$: Observable<Score[]>;

  constructor(
    private http: HttpClient,
    private mainService: MainService
  ) {
    // set a timer for every 5 seconds, starting now, to get scores
    this.scores$ = this.mainService.product$.pipe(
      switchMap(() => timer(0, 5000)),
      switchMap(() => this.getScores())
    );

    this.mainService.product$.subscribe((product) => {
       this.product = product;
    }
  }

  getScores(): Observable<Score[]> {
   // ...
  }
}

Такое ощущение, что вам не следует использовать свойство продукта в этой услуге, и пусть все это будет потоками. Это предотвратит ненужные подписки. Вы также можете добавить shareReplay, чтобы любые последующие подписки получали самое последнее значение от http-вызова:

export class ScoresService {
  readonly product$: Observable<Product> = this.mainService.product$;

  readonly scores$: Observable<Score[]> = this.product$.pipe(
    switchMap((product) => timer(0, 5000).pipe(
      switchMap(() => this.getScores(product.id))
    )),
    shareReplay({ refCount: true, bufferSize: 1 })
  ); 

  constructor(private http: HttpClient, private mainService: MainService) {}

  getScores(id: number): Observable<Score[]> {
   // ...
  }
}
...