Как правильно реализовать условный оператор debounceTime () с использованием оператора iif ()? - PullRequest
0 голосов
/ 17 июня 2020

У меня есть BehaviourSubject, на который подписан через getSaveBehaviorSubject () в конструкторе Service. Этот объект поведения может получать данные нескольких типов от других компонентов / служб, некоторые из которых должны храниться в бэкэнд, а другие нет. Когда он получает значение, которое необходимо сохранить, он отправляет его на серверную часть через запрос put/post. В качестве ограничителя скорости запросов я использую оператор debounce(300), чтобы фактический запрос к серверу выполнялся после того, как прошло 300 мс бездействия. Это предотвратит выдачу сервером ошибки 429 Too many requests. Оператор противодействия должен применяться только тогда, когда передается тип значения, который необходимо сохранить. Я пробовал создать такое условие с помощью оператора iif(). Мой код выглядит так:

this.myService.getSaveBehaviorSubject()
.pipe(
  tap(pair => {
    if(this.dataStoreServiceJustInitialized){
      this.dataStoreServiceJustInitialized = false; 
      return;
    }

    if (pair.length > 1){
        this.updateDataTypeValue(pair);
        localStorage.setItem(pair[0], JSON.stringify(pair[1]));
        if(pair[0] === DataType.MEASURE_SYSTEM_PREFERENCE)
            this.adjustSearchRadiusToChangedMeasureSystem();
    }
  }),
  mergeMap(pair =>
    iif(() => !!(this.loggedInSubject.value && this.dataTypeExclusionList.includes(pair[0])), 
    of(pair).pipe(debounceTime(2000)), of(pair)
    )))
.subscribe(pair => {If (this.loggedInSubject.value) // make request to server...}

Мое решение основано на этом руководстве: https://www.learnrxjs.io/learn-rxjs/operators/conditional/iif, а интересующий код начинается с mergeMap.

Если пользователь вошел в систему, и тип данных должен быть сохранен в базе данных (не содержится в dataTypeExclusionList), тогда iif() должен возвращать наблюдаемое с конвейером, содержащим оператор debounceTime. В противном случае он должен просто вернуть новый наблюдаемый объект с исходным значением.

Когда условие iif истинно, debounceTime, похоже, не работает. Независимо от того, вошел ли я в систему или нет, или если тип данных должен быть сохранен или нет, он всегда будет отправлять запрос немедленно. Ясно, что я делаю что-то не так.

Может ли кто-нибудь сказать мне, как правильно использовать оператор iif в сочетании с оператором debounceTime? Спасибо

1 Ответ

0 голосов
/ 17 июня 2020

Здесь есть две проблемы:

  1. debounceTime будет выдавать свое текущее значение сразу после завершения его источника - без ожидания тайм-аута. Фабрика of создаст одно уведомление и сразу после этого завершит его. Таким образом, не происходит никакого отскока. Вы можете исправить это, заменив debounceTime на delay, так как он будет ждать выхода, даже если источник уже завершен.

  2. Вы используете mergeMap, который не будет отказаться от подписки на предыдущий Observable, когда он получит новое уведомление. Поэтому у вас может быть случай, когда getSaveBehaviorSubject излучает 100 раз в течение тайм-аута, и вы отправляете 100 запросов к серверу. Вы можете исправить это, заменив mergeMap на switchMap.

Применение обоих должно решить вашу проблему.

Но я бы порекомендовал другой подход: используйте оператора debounce. Вместо значения тайм-аута вы передаете функцию, которая возвращает Observable. Когда этот Observable излучает, текущее значение будет перенаправлено.

Вот так:

this.myService.getSaveBehaviorSubject().pipe(
  tap(...),
  debounce(pair => {
    if(... debounce condition ...) {
      return timer(2000);
    } else {
      return timer(0);
    }
  }
}
...