Введение

Эта статья относится к серии под названием «Angular встречается с RxJS», в которой я пытаюсь объяснить реактивное программирование с использованием «RxJS» в контексте «Angular» как можно лучше.

Оглавление

Основные понятия
Темы RxJS
Операторы RxJS (Часть 1)
Операторы RxJS (Часть 2)
Операторы RxJS (Часть 3)
takeUntil и асинхронный канал»
Наблюдаемые более высокого порядка
Обработка ошибок
Планировщики RxJS (скоро)
Мини-проект: Сборка Pokedex (вскоре)

В предыдущей статье…

… мы увидели несколько популярных и полезных операторов «RxJS». Мы продолжим этот путь в этой статье, где я представлю несколько других очень интересных операторов. Еще раз, если вы чувствуете, что я забыл что-то важное или не совсем понял, сообщите мне об этом в комментарии, и я улучшу статью.

срывать

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

В этом примере мы испускаем значение свойства «длина» для каждого испускаемого значения.

Обратите внимание, что вы можете перемещаться по вложенным свойствам, передавая оператору несколько имен свойств. Например:

В этом примере будут получены значения «2» и «undefined», поскольку свойство «bar» не существует в «obj2».

Мраморная диаграмма этого примера выглядит следующим образом:

начать с

Оператор «startWith» может использоваться для выдачи значения перед первым значением наблюдаемого источника. Это очень полезно, когда вы подписываетесь на наблюдаемый объект, который не выдает значение при подписке, таким образом, вы можете выдать значение по умолчанию перед обработкой значений, выдаваемых самим наблюдаемым источником.

В этом примере наблюдаемый источник выдает значения от «1» до «10» после того, как значение «0» выдается оператором «startWith».

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

Мраморная диаграмма выглядит следующим образом:

задерживать

Вы можете отсрочить выдачу значений с помощью оператора «задержка»:

В этом случае первое значение «диапазона» выдается с задержкой в ​​одну секунду.

Этот оператор может быть довольно интересным в контексте «Angular», чтобы дождаться выполнения цикла обнаружения изменений. Вы, вероятно, знакомы с такими ситуациями, когда вы хотите обновить значение после обновления представления. В большинстве случаев вы используете «setTimeout» с продолжительностью «0», чтобы отложить выполнение вашего кода после выполнения цикла событий. Вы можете добиться того же, используя оператор «задержка» со значением «0».

Мраморная диаграмма примера выглядит следующим образом:

буферный счетчик

Оператор «bufferCount» буферизует определенное количество значений, прежде чем передать их в виде массива. Например:

Приведенный выше код буферизует выданное значение, и когда размер буфера достигает «2», выдается массив с буферизованными значениями.

Вы также можете передать другой параметр этому оператору, чтобы использовать его при запуске буфера. Например, вы можете сказать, что хотите буферизовать значения по «3», но хотите, чтобы запуск буферов происходил через каждые «2» значения:

В этом случае вы получите несколько массивов из трех значений, испускаемых наблюдаемым выходом. Однако, поскольку вы определили параметр «startBufferEvery» на «2», буферы будут запускаться через каждые два значения.

Итак, первый буфер начинается с «0» и содержит три значения: «1», «2» и «3». Поскольку вы определили второй параметр «bufferCount» равным «2», следующий буфер будет начинаться не с «4», а с «3». Действительно, этот параметр указывает «bufferCount» запускать буферизацию через каждые два значения, то есть после «1» и «2», то есть в «3». И так далее…

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

Мраморная диаграмма первого примера выглядит следующим образом:

сканирование

Оператор «scan» со временем уменьшает выдаваемые значения. Это довольно сложно объяснить без примера, поэтому давайте посмотрим на следующий:

Приведенный выше код применяет редуктор (первый параметр) для каждого испускаемого значения. «acc» — это то, что мы называем «аккумулятором», а «value» — это испускаемое значение. Давайте разобьем его на шаги, чтобы было кристально ясно.

  • Выдается первое значение («1»).
  • Редуктор выполняется и суммирует выданное значение с текущим значением аккумулятора. Поскольку перед текущим значение не было передано, начальное значение («0») используется в качестве значения для аккумулятора, поэтому выдаваемое значение равно «0» + «1», то есть «1».
  • Выдается следующее значение («2»).
  • Редуктор выполняется и суммирует выданное значение с текущим значением аккумулятора. Поскольку значение уже было выпущено, значение аккумулятора — это ранее выпущенное значение выходной наблюдаемой («1»), поэтому испускаемое значение равно «2» + «1», поэтому «3».
  • Выдается следующее значение («3»).
  • Значение аккумулятора теперь равно «3» (поскольку это последнее значение, испускаемое наблюдаемым выходом), поэтому следующее испускаемое значение равно «3» + «3», то есть «6».
  • И так далее…

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

Мраморная диаграмма этого примера:

уменьшать

Оператор «reduce» ведет себя точно так же, как «scan», за исключением того, что он выдает окончательное значение только после завершения исходной наблюдаемой.

Выполнение этого кода точно такое же, как его эквивалент с оператором «scan», однако только последнее значение выдается, когда исходная наблюдаемая завершается. Это означает, что если вы используете бесконечный исходный наблюдаемый объект (например, используя «интервал»), вы никогда не получите значение. Результат приведенного выше кода:

И мраморная диаграмма примера следующая:

debounceTime

Этот оператор не будет испускать значения, испускаемые наблюдаемым источником, до тех пор, пока не наступит определенный период времени без испускания значений. Как правило, это оператор, который вы хотите использовать в таких компонентах, как «упреждающий ввод», где вы инициируете HTTP-вызов для поиска того, что пользователь вводит в элементе ввода. Для этого вы хотите избежать вызова REST API для каждого введенного символа. «debounceTime» может помочь вам в этом, так как он будет ждать в течение определенного периода времени без передачи какого-либо испускаемого значения, прежде чем выдать самое последнее значение.

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

Как видно из приведенного ниже результата, движения курсора регистрируются в консоли только тогда, когда курсор останавливается более чем на 100 миллисекунд.

Таким образом, в основном, когда значение «A» испускается наблюдаемым источником, оператор «debounceTime» запускает таймер. Когда выдается значение «B», оператор сравнивает прошедшее количество миллисекунд со значением, переданным в параметре. Если первый меньше второго, значение «А» отбрасывается и таймер сбрасывается. Только когда количество прошедших миллисекунд больше, чем значение, указанное в параметре, значение выдается выходным наблюдаемым.

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

SampleTime

Оператор «sampleTime» просто проверяет каждые «X» миллисекунд, было ли выдано значение или нет с момента предыдущей проверки, и если да, то выдает его. Взгляните на следующий пример:

В этом примере оператор «sampleTime» каждую секунду проверяет, выдало ли исходное наблюдаемое значение значение или нет с момента последней проверки. Первая проверка выполняется через одну секунду, и в этот момент исходная наблюдаемая уже выдала два значения: «0» и «1», поэтому «1» выдается выходной наблюдаемой. Исходный наблюдаемый затем выдает значение «2» через 1,4 секунды в целом, поэтому, когда оператор «sampleTime» проверяет через две секунды (в целом), было ли выдано новое значение с момента последней проверки, он находит «2 ” и излучает его. Теперь, через 100 миллисекунд после испускания «2», наблюдаемый источник испускает «3», а затем через 700 мс после «4». Другими словами, значение «4» выдается через 2,8 секунды в целом, поэтому, когда «sampleTime» выполняет свою третью проверку через три секунды в целом, оно выдает значение «4», а не «3».

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

буферное время

Оператор «bufferTime» ведет себя как «sampleTime», но выдает все сгенерированное значение вместо последнего. Давайте возьмем тот же пример, что и выше, и заменим «sampleTime» на «bufferTime».

С этим оператором результат следующий:

По сути, объяснение точно такое же, как и для «sampleTime», за исключением того, что вместо того, чтобы выдавать только последнее значение, «bufferTime» выдает массив со всеми выданными значениями.

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

дроссельВремя

Оператор «throttleTime» выдает значения, а затем игнорирует следующие в течение указанного периода времени. Например:

В этом примере исходный наблюдаемый объект начинает с выдачи значения «0», которое перенаправляется на выходной наблюдаемый оператор «throttleTime». Затем оператор начинает игнорировать значения в течение одной секунды, поэтому он не будет пересылать значение «1», выданное наблюдаемым источником через 700 мс. Однако он будет выдавать значение «2», выдаваемое через 1,4 секунды в целом. Период игнорирования начинается снова, как только выдается значение, поэтому значение «3» не перенаправляется на выходную наблюдаемую.

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

аудитВремя

«auditTime» очень похож на «throttleTime», за исключением того, что вместо первого выданного значения, когда начинается период игнорирования, он выдает самое последнее значение, когда период игнорирования заканчивается. Давайте обновим предыдущий код, заменив «throttleTime» на «auditTime».

Исходный наблюдаемый объект начинает с того, что сразу выдает «0», что запускает период в одну секунду, в течение которого «auditTime» не пересылает испускаемые значения. Этот период все еще продолжается, когда значение «1» выдается через 700 мс и через 300 мс после того, как период закончился, «auditTime» пересылает самое последнее выданное значение наблюдаемым источником: «1». Этот процесс повторяется, как только наблюдаемый источник испускает новое значение.

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

Последние мысли

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

Следующая статья будет посвящена другому оператору: «takeUntil», чтобы показать, как вы можете использовать его в контексте «Angular», чтобы резко сократить количество раз, когда вы вызываете метод «отписаться» для освобождения ресурсов.

Затем мы поговорим о некоторых более сложных операторах, используемых для возврата наблюдаемых объектов и управления их подписками.