Разница между debounceTime и throttleTime

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Цель

В этой статье я хочу рассказать тонкую разницу между операторами debounceTime и throttleTime простыми словами


Общее

1) У нас есть метод в сервисе нашего Angular приложения

getData(id: number): Observable<RxjsResponse> {
  return this.httpClient.get<RxjsResponse>(`${this.basePath}/data/${id}`);
}

Это простой метод, который обращается на backend и получает ответ в виде

{
  id: number;
  name: string;
}

2) Есть backend нашего приложения

justData.get('/data/:id', async (req: Request, res: Response): Promise<void> => {
    const id = req.params.id;

    if (id === '1') {
        res.status(200).send({
            id: 1,
            name: 'Первый'
        });
    }
    if (id === '2') {
        res.status(200).send({
            id: 2,
            name: 'Второй'
        });
    }
    if (id === '3') {
        res.status(200).send({
            id: 3,
            name: 'Третий'
        });
    }
    if (id === '4') {
        res.status(200).send({
            id: 4,
            name: 'Четвёртый'
        });
    }
});

Это простой роут на бэке, который в зависимости от id возвращает определённый ответ


debounceTime

(Ну что, успокоился?)

Посмотрим как работает debounceTime, он проще для понимания

interval(1000)
  .pipe(
    take(5),
    filter((id) => id !== 0),
    debounceTime(1500),
    mergeMap((id) => this.rxjsService.getData(id)),
    tap((result) => console.log(result))
  )
  .subscribe();

Что тут происходит?

  • interval выкидывает целое число (0, 1, 2, 3......) каждую секунду

  • take(5) отпишется от interval после пяти эмитов (до следующего оператора filter дойдут только числа от 0 до 4)

  • filter((id) => id !== 0) не пропустит первый эмит (до следующего оператора debounceTime дойдут только idшки от 1 до 4)

А теперь рассмотрим что происходит после фильтра.

Оператор debounceTime не пропустит ни один эмит, к mergeMap, пока после предыдущего эмита не пройдёт то кол-во времени, которое указано в операторе. То есть в данном случае, когда единица дойдёт до debounceTime мы должны будем подождать 1.5 секунды, чтобы пройти с ней к mergeMap, но, так как у нас эмиты происходят каждую секунду, единица так и не пройдёт дальше, придёт двойка, поэтому про единицу можно забыть и так далее до конца. Догадываетесь какой будет результат?

Алгоритм:

  • Эмит 0 -> filter не пропустил

  • Эмит 1 -> id = 1 дошла до debounceTime, ждём полторы секунды

  • Эмит 2 -> id = 2 сэмитилась быстрее, чем время ожидания от debounceTime, забываем про id = 1, ждём 1.5 секунды с id = 2

  • Эмит 3 -> id = 3 сэмитилась быстрее, чем время ожидания от debounceTime, забываем про id = 2, ждём 1.5 секунды с id = 3

  • Эмит 4 -> id = 4 сэмитилась быстрее, чем время ожидания от debounceTime, забываем про id = 3, ждём 1.5 секунды

  • Больше эмитов нет, проходит 1.5 секунды, id = 4 двигается к mergeMap, делаем запрос на сервер

// Ответ
{
  "id": 4,
  "name": "Четвёртый"
}

Самый банальный пример использования: у вас есть input, который отвечает за поиск на сервере и отсылает запрос при изменении значения в input. Пользователь каждую секунду в этот поиск вносит новый символ. Зачем вам реагировать на каждое изменение и грузить сервер, когда можно отмести все лишние значения и уже когда пользователь перестанет вводить символы, пройдёт n секунд, уже пустить запрос на сервер?


throttleTime

(Открыть шлюз! Закрыть шлюз!)

interval(1000)
  .pipe(
    take(5),
    filter((id) => id !== 0),
    throttleTime(1900),
    mergeMap((id) => this.rxjsService.getData(id)),
    tap((result) => console.log(result))
  )
  .subscribe();

Код тот же самый, что и в предыдущем примере, только теперь у нас throttleTime(1900).

Оператор throttleTime, после первого пройденного через него эмита, не будет пропускать следующие эмиты в течении 1.9 секунд. То есть в данном случае, когда единица пройдёт через throttleTime, клапан закроется на 1.9 секунд и, например, следующий эмит, который произойдёт через секунду к mergeMap не пройдёт, а третий уже пройдёт, потому что он будет сделан через 2 секунды, клапан к тому времени уже будет открыт, а потом снова закроется на 1.9 секунды

Алгоритм:

  • Эмит 0 -> filter не пропустил

  • Эмит 1 -> id = 1 прошёл через throttleTime, клапан закрылся, пошёл запрос на сервер

  • Эмит 2 -> id = 2 доходит до throttleTime, клапан закрыт, не проходит дальше

  • Эмит 3 -> id = 3 доходит до throttleTime, клапан открыт, проходит в mergeMap, клапан закрылся, пошёл запрос на сервер

  • Эмит 4 -> id = 4 доходит до throttleTime, клапан закрыт, не проходит дальше

//Ответ
{
    "id": 1,
    "name": "Первый"
}
{
    "id": 3,
    "name": "Третий"
}

Ещё один банальный абстрактный пример: вам нужно ограничить количество кликов по кнопке. Пользователь кликает по пять раз в секунду, а вам нужно пропускать только один клик в секунду, throttleTime(1000) вам в помощь


Заключение

Для тех, кто хочет сам потыкать на кнопки и посмотреть как работают операторы вот проекты на github. Поизменяйте время в операторах, почувствуйте как это работает

Фронт часть

Бэк часть

Это всё. Надеюсь, статья была для вас полезной.

Спасибо :)

Источник: https://habr.com/ru/post/715972/


Интересные статьи

Интересные статьи

Уже очень давно программное обеспечение стало настолько масштабным, что его приходится разбивать на части. Каждая часть выполняет свою задачу, но как они общаются между собой? Как разные части систе...
Долгое время беспроводные сети WiFi предполагали наличие проводной сети между точками доступа. Даже название «точка доступа» предполагает, что эти устройства работают на уровне доступа для подключен...
Меня зовут Катя, я недавно стала QA-лидом в Альфа-Банке, до этого работала в команде. В этом посте я расскажу, как у меня происходил переход из командного тестировщика в позицию QA-лида. Возможно, моя...
Все «за» и «против» 1С-Битрикс, какие есть альтернативы и что выгоднее знать разработчику? Читать далее
Я давно знаком с Битрикс24, ещё дольше с 1С-Битрикс и, конечно же, неоднократно имел дела с интернет-магазинами которые работают на нём. Да, конечно это дорого, долго, местами неуклюже...