December 19, 2022

RxJS - шпаргалка

Операторы объединение Observable.

merge - слияние

Оператор слияния merge используется, когда мы хотим создать поток, который генерируется всякий раз, когда наблюдаемый источник (один из множества) выдает значение.

Например, если нужно отслеживать активность пользователя,чтобы выйти из системы или показать уведомление. Чтобы сделать это, нам нужно отслеживать активность пользователя, такую как клики, прокрутки, щелчки правой кнопкой мыши и тому подобное, и действовать, когда в течение определенных периодов времени не происходило никаких событий. Вот пример кода:

const ACTIVE_EVENTS = [
  'click', 'scroll', 'contextmenu', 'dblclick', 'mousemove',
];
// можно добавить любые события, для определения неактивности пользователя

merge(...ACTIVE_EVENTS
.map(event => fromEvent(document, event)))
.pipe(
bufferWhen(() => interval(10000)),
filter(events => events.length === 0),)
.subscribe(() => alert('You have been inactive for ten seconds!'))

Тут создаются несколько потоков (используя FromEvent) для отслеживания различных событий браузера, которые могут указывать на активность пользователя, а затем объединяем их в один поток, чтобы действовать, когда какое-то время событий не было. Тип событий не важен, важен факт, что событие произошло.

Если важен сам факт наступления события, придется использовать “слияние” merge.

bufferWhen(closingSelector: Observable): Observable

bufferWhen - собирает данные из источника, наблюдаемого до тех пор, пока функция closingSelector не закроет буфер.

closingSelector - фабричная функция закрытия Observable, чтобы указать, когда закрывать, испускать и сбрасывать буфер.

В данном случае, каждые 10000 мс происходит эммит значений.

Отличие buffer от bufferWhen

Оператор buffer принимает второй наблюдаемый объект в качестве аргумента и будет буферизовать значения из первого наблюдаемого объекта до тех пор, пока второй объект не будет передан. Затем buffer сбрасывается и начинает буферизацию снова, пока второй наблюдаемый объект не будет передан еще раз.

bufferWhen похож на buffer, но вместо того, чтобы принимать наблюдаемый объект, он принимает функцию селектора (так называемый закрывающий селектор).

Т.е. buffer эммитит значение, когда приходит значение наблюдаемого объекта, а bufferWen эммитит значение, когда выполняется функция.

Есть еще:

- bufferCount - позволяет задавать количество значений, которые нужно хранить в буфере, прежде чем они будут переданы.

- bufferTime - принимает количество миллисекунд для буферизации значений. По истечении времени буферизованные значения передаются, а буфер запускается снова.

- bufferToggle - принимает два аргумента: наблюдаемый объект, чтобы начать буферизацаию, и закрывающий селектор, чтобы остановить ее и выдать значения.

combineLatest

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

Например, в зависимости от ввода значений в поля, с сервера приходят признаки обязательности для других полей и происходит перестройка интерфейса.

combineLatest эммитит значение, когда какое-либо из отдельных потоков эмитит значение.

// получаем данные от сервера - например, перечень
// динамических компонентов 
dynamicControls$ = this.controlsService.getDynamicControls();

formValue$ = combineLatest([
        this.form.valueChanges,
        this.dynamicControls$,
    ]).pipe(
        tap(([value]) => {
// тут происходит установка валидатора для полей.
            if (value.attachmentsRequired) {
                this.controls.attachments
                    .setValidators(Validators.required);
            } else {
                this.controls.attachments.clearValidators();
            }
        }),
        map(([value, controls]) => {
            const controlsValue = { ...value, ...controls };
            return controlsValue;
        }),
    );

forkJoin

Если необходимо получить несколько наборов данных, которые извлекаются из разных API, нужно использовать forkJoin, чтобы дождаться, пока все данные будут доступны, и только после этого что-то делать в пользовательском интерфейсе.

homePageData$ = forkJoin([
    this.userService.getUserInfo(),
    this.dataService.getData(),
    this.otherDataService.getOtherData(),
]).pipe(
    map(([userInfo, data, otherData]) => ({
        userInfo,
        data,
        otherData,
    })),
    catchError(error => of({/*error object*/})),
);

pairwise

Объединяет предыдущее и текущее значение потока.

Например, когда нужно в предварительно заполненной форме проверить изменения.

Для простых случаев можно использовать состояние формы dirty, но если необходимо учитывать не только факт изменения в полях, а еще чтобы старое значение не совпадало с новым, то можно использовать pairwaise.

disabled$ = this.form.valueChanges.pipe(
    pairwise(),
    map(([prev, current]) => {
        return this.utilitiesService.isEqual(prev, currentnged
    }),
);

withLatestFrom - "с последним из"

Объединяет два потока, причем из потока withLatestFrom эммитит только последнее значение.

Когда основной поток выкидывает значение, то он просит withLatestFrom дать последнее значение которое у него было.

this.authService.login(credentials).pipe(
    withLatestFrom(
      this.route.queryParamMap.pipe(startWith(new Map())),
    ),
).subscribe(([, params]) => {
    if (params.get('redirectUrl')) {
        const navUrl = params.get('redirectUrl') ?? '/home';
        this.router.navigateByUrl(decodeURIComponent(navUrl));
    }
});

Необходимо перенаправить со страницы при успешном входе в систему, но только при наличии параметра запроса “redirect_url”. Мы можем взять это значение из наблюдаемого параметра queryParamMap, но мы не хотим запускать перенаправление, когда параметр запроса изменяется по какой-либо причине, только после завершения успешного HTTP-вызова login.

Действие не будет выполнено при изменении параметров запроса, а только при успешном завершении вызова login.

debounceTime, throttleTime, auditTime

debounceTime - эмитит значение из Observable, только после того, как прошел определенный промежуток времени. Использовать, если нужно реализовать автокомплит или упреждаюдищий ввод ( typeahead ).

throttleTime - эмитит значение из Observable, затем игнорирует последующие значения источника в течение миллисекунд, затем повторяет этот процесс. Использовать, если нужно реализовать ограничение частоты кликов, обработку двойного клика.

auditTime - эмитит самое последнее значение за указанный период времени.

retry

Простейший случай:

responseFromServer$.pipe(
    retry(3), // делаем три попытки
); // после 3 неудачных попыток обрабатываем ошибку.

Ожидание нажатия кнопки с оператором повторов:

responseFromServer$.pipe(
    retry({
        count: 3, // можем указать опциональное количество
                  // сколько раз пользователь может 
                  // повторить попытку
        delay: () => fromEvent(
              document.querySelector('#retryBtn'),
              'click',
        ), // ожидаем нажатия кнопки
    }),
);

https://dev.to/this-is-learning/understanding-rxjs-use-cases-part-ii-51ll оригинал статьи.