Если вы используете библиотеку RxJS в своих проектах, вы должны быть знакомы с подпиской и отказом от подписки на наблюдаемый объект. Кроме того, вы должны знать, что сила RxJS проистекает из его операторов. Оператор принимает входной поток и возвращает выходной поток значений. Если вы хотите спроецировать значение, испускаемое исходным потоком, вы используете оператор map и предоставляете функцию проекта. С другой стороны, если вы хотите создать наблюдаемое на основе наблюдаемого значения источника, вам необходимо использовать один из следующих операторов: mergeMap, concatMap, switchMap, выхлопная карта. Более того, оператор switchMap позволяет приостанавливать и возобновлять наблюдаемое.

Описание проблемы

Пару недель назад мне впервые довелось использовать паттерн с паузируемым наблюдаемым. Я был вовлечен в существующий проект Angular и должен был исправить несколько ошибок. Честно говоря, я был поражен, когда впервые открыл проект в WebStorm. Все в одном модуле, странное соглашение об именах и так далее… Но худшее было еще впереди. Первая ошибка, которую необходимо исправить, была описана следующим образом: непрерывное нажатие кнопки изменения упражнения приводит к ошибке. Одним словом, приложение представляет собой своего рода игру для школьников, нацеленную на улучшение математических навыков. Упражнения разделены на разделы, и в окне просмотра упражнений можно изменить упражнение, которое было источником ошибки, которую мне пришлось исправить. Состояние приложения управляется с помощью ngrx / store. Задача казалась простой для решения, а именно обнаружение действия, отправляемого после нажатия кнопки изменения упражнения, и отключение кнопки, пока изменение упражнения еще не выполнено. Я открыл redux devtools и нажал кнопку, чтобы узнать название интересующего меня действия. К моему удивлению, желание изменить упражнение привело к отправке около 20 действий! до рендеринга нового упражнения. Я понял, что потребуется много времени, чтобы ознакомиться со всем, что происходило в процессе, поэтому я выбрал другой подход. Я хотел сообщить магазину о нажатии кнопки только тогда, когда процесс изменения упражнения еще не отложен. Посмотрим, как оператор switchMap сэкономил мне время!

Академический пример

Моя первая идея решить вышеупомянутую проблему заключалась в использовании оператора вытяжка. Однако быстро выяснилось, что при таком подходе можно обрабатывать только самые простые сценарии. Почти как в университете, а именно: вас учат работать с наиболее очевидными вариантами использования. Напротив, давайте посмотрим на практический пример, в котором всю работу выполнял оператор выхлопной карты.

Живой пример доступен здесь: https://jsfiddle.net/1v785cm6/29/

Шаблон просто состоит из двух полей ввода и кнопки входа в систему.

Я начинаю с создания наблюдаемых из событий ввода в полях username и password (строки 14 и 18 соответственно). Затем я создаю комбинированный наблюдаемый объект для получения учетных данных пользователя (строка 22). При каждом нажатии кнопки входа в поток loginRequested $ отправляется значение, включающее учетные данные пользователя. Обратите внимание, что значение не отправляется, пока не будут отправлены все наблюдаемые (имя пользователя $ и пароль $), создающие учетные данные $ наблюдаемого. Правильная отправка формы приводит к поддельному вызову входа в систему, который возвращает токен сеанса. Использование вытяжнойMap не вызовет другой HTTP-вызов, если предыдущий ожидает, а именно, текущая внутренняя наблюдаемая не завершена. Попробуйте постоянно нажимать кнопку входа в систему и убедитесь, что ведется только один журнал консоли, пока ожидает поддельный запрос. После получения ответа (токена) повторное нажатие кнопки входа в систему приводит к новому HTTP-запросу.

Pausable наблюдаемый

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

Живой пример доступен здесь: https://jsfiddle.net/Ldryh7v0/23/

Шаблон - это максимально простой шаблон, а именно кнопка для отправки следующего уведомления и кнопки паузы / возобновления.

Я начинаю с создания основных наблюдаемых:

  • notifications $, которое выдает значение при нажатии кнопки уведомления,
  • pauseBtnClick $, который выдает значение false при нажатии,
  • resumeBtnClick $, который выдает значение true при нажатии.

Затем мне нужно получить информацию о том, заинтересован ли пользователь в уведомлениях или нет (наблюдаемый isInterest $). Я предполагаю, что изначально пользователь желает слушать уведомления (startWith (true)).

Наконец, поток уведомлений, доходящих до пользователя, создается следующим образом. Я подписываюсь на isIntehibited $, поскольку это источник информации о том, что мне интересно получать уведомления. Если пользователь передумает, значение будет выдано наблюдаемым. Затем на основе флага isIntehibited я подписываюсь на поток notifications $ (источник новых сообщений) или на наблюдаемый объект, который никогда не отправляется и немедленно завершается (empty ( )). Такой подход позволяет временно избавиться от новых значений в результирующем потоке. Как раз то, что мне нужно!

Моя жизнь в действии

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

Первоначальный ngrx / эффект, в котором я был заинтересован, выглядел следующим образом

Короче говоря, каждое действие типа ExerciseActionTypes.Change приводило к отправке еще трех действий в хранилище (источник остальных 17 действий). Как правило, эффекты являются источником действий, отправляемых в хранилище, поскольку он неявно подписывается на каждый наблюдаемый объект с помощью декоратора Effect, если он не имеет объекта конфигурации {dispatch: false}. предоставлена.

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

У меня было все необходимое:

  • ExerciseActionTypes.Change указывает состояние "не беспокоить",
  • ExerciseActionTypes.LoadSuccess указывает, что приложение готово к другой нагрузке.

Я создал наблюдаемый предикат shouldPause $, который генерирует логическое значение на основе типа действия. Новое значение было отправлено только при отправке одного из вышеупомянутых действий. Обратите внимание, что хранилище не было подписано на наблюдаемое и не интересовалось значениями из потока changeExercise $. Хранилище получило действия от наблюдаемого объекта pausableChangeExercise $, которые не выдавали значения, когда процесс выполнения изменений ожидался. Я выжил!

Выводы

Надеюсь, вам понравилась статья, и вы нашли новый вариант использования оператора switchMap очень привлекательным. Оказывается, помимо подписки на наблюдаемый объект и отказа от подписки на него, вы также можете приостановить и возобновить поток.

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

В следующей статье из этой серии я собираюсь рассказать о маленькой RxJS Gang Of Four, а именно о mergeMap, concatMap, switchMap, вытяжка.