Реактивное программирование
Объяснение RxJs на простом английском
И 7 наиболее часто используемых операторов, о которых вы должны знать
Вступление
Вы хотите начать изучать RxJ, но запутались в терминологии, которая его окружает? Или вы уже пользуетесь им и хотите проще разобраться?
В любом случае вы попали в нужное место. В этом посте вы узнаете
- терминология, используемая в RxJs
- роли, которые играют Наблюдатель, Наблюдаемый, Подписчик и Оператор, и их обязанности
- как создать Observable с помощью различных методов.
- и семь наиболее часто используемых операторов и как они работают
В стране RxJ есть, что рассказать, но этот пост должен помочь вам понять, что происходит в библиотеке.
Примечание. Все примеры, использованные в этом посте, можно найти в Репозитории Git, объясняющем RxJs.
Терминология
В RxJS есть несколько ключевых игроков, о которых вам следует знать
- Наблюдаемый - производит последовательность (поток) данных.
- Наблюдатель - потребляет наблюдаемые значения
- Подписчик - связывает наблюдателя с наблюдаемым.
- Оператор - преобразования значений на маршруте
- Тема - включает как наблюдаемого, так и наблюдателя.
Примечание. Я не собираюсь углубляться в темы в этом сообщении. Существуют разные типы тем, и их довольно много, чтобы рассказать об этом в другом сообщении.
Аналогия с Netflix
Все эти термины поначалу может быть довольно сложно понять, поэтому давайте рассмотрим аналогию, чтобы лучше понять, какова роль каждого игрока в общей схеме вещей.
Netflix - продюсер контента. У компании есть веб-сайт и мобильное приложение, которые делают их контент наблюдаемым. Я становлюсь подписчиком на сервис, создавая учетную запись, и как только я подписываюсь на Netflix, моя семья может наблюдать за контентом, который они создают.
Observable, в данном случае приложение, может вызывать определенные действия в Observer, my family. Эти действия
- next () - для показа и воспроизведения следующего выпуска в ленте
- error () - чтобы уведомить наблюдателя при возникновении ошибки
- complete () - для уведомления наблюдателя, когда сезон или серия завершены
Как подписчик я должен предоставить Observer, кого-то, кто будет наблюдать за контентом, в Observable, чтобы Observable мог вызывать методы next()
, error()
и complete()
в Observer.
Принимая во внимание аналогию с Netflix, у Observable есть следующие обязанности:
- Разрешить подписку и отписку на его поток
- Отправьте значение next в Observer
- Если что-то пойдет не так, сообщите наблюдателю об ошибке.
- И уведомить наблюдателя, когда поток завершен.
Наблюдатель же может решить, как
- обрабатывать следующее значение из Observable
- обрабатывать любые ошибки, исходящие от Observable
- обработать, когда Observable сообщает, что поток данных завершен
предоставляя функцию для каждого из них.
Если бы мы визуализировали это на диаграмме, вот как бы это выглядело
Создание наблюдаемого с помощью RxJs
В этом разделе мы рассмотрим несколько методов, которые RxJs предоставляет для создания Observable. Сначала я перечислю их, а затем мы углубимся в код.
- из()
- Observable.create () или (
new Observable(fn)
) - of()
Но прежде чем мы перейдем к созданию Observable, давайте сначала посмотрим, как выглядит Observer.
Как упоминалось выше, Observer выбирает, как обрабатывать методы next()
, error()
и complete()
, которые вызываются Observable.
Примечание. Нет необходимости реализовывать все три метода в Observer. По крайней мере, наблюдатель должен уметь обрабатывать next
метод.
Функция from ()
Этот метод способен преобразовывать множество вещей в Observable. Это включает, помимо прочего, массивы, обещания и итерируемые объекты.
Примечание. В приведенном выше примере я явно определил объект Observer, реализующий методы next()
, error()
и complete()
, и передал его методу подписки. RxJs также позволяет нам передавать их как функции непосредственно в метод подписки.
Обратите внимание на следующее.
При работе с обещаниями функция from()
возвращает Observable, который выдает значение разрешенного обещания. Если обещание разрешено, то разрешенное значение выдается из Observable, в противном случае выдается ошибка отклонения из обещания.
Observable.create ()
Метод create()
принимает функцию в качестве параметра и преобразует ее в Observable. Эта функция запускается каждый раз, когда Observer подписывается на этот Observable. Параметр функции называется onSubscription
.
По сути, это позволяет нам определить, что произойдет, когда Observer подписывается на этот Observable. Это делает нашей обязанностью вызывать методы next()
, error()
и complete()
объекта Observer внутри функции onSubscription
.
Функция onSubscription
вызывается с Observer
в качестве единственного параметра.
Примечание. Метод create()
устарел, и вместо него следует использовать new Observable()
конструктор. В следующем примере я использую конструктор new Observable()
, но в файлах кода вы можете увидеть использование Observable.create()
.
Функция of ()
Эта функция принимает любое количество аргументов и возвращает Observable, который испускает каждый аргумент как наблюдаемую последовательность.
const { of } = require('rxjs'); of(1, 2, 3).subscribe(data => console.log(data));
Примечание. Здесь я привел довольно простой пример, который также можно найти в документации RxJs. Позже в этом посте вы увидите другие способы использования этого.
В этом разделе мы рассмотрели, как создать Observable, используя три разных метода. Конечно, есть и другие способы, которые мы здесь не рассмотрели, но вы можете просмотреть Документы RxJs. Вы можете найти их под заголовком Операторы создания.
В следующем разделе мы рассмотрим наиболее часто используемые операторы, которые предоставляет RxJs.
Операторы RxJs
… Это просто функции.
Не более того.
Это функции с четко определенными входами и выходами. Оператор принимает Observable в качестве входных данных и производит Observable в качестве выходных данных. Они действуют как функция преобразования, которая преобразует данные, проходящие через них.
Возьмем, к примеру, класс Array, у него есть встроенный метод map()
. Этот метод принимает функцию в качестве параметра и возвращает новый массив с преобразованными значениями.
Точно так же RxJs предоставляет оператора с именем map()
. Как и метод map()
из Arrays, он также принимает функцию в качестве параметра, но оператор возвращает Observable, на который вы затем можете подписаться.
Есть два типа операторов
- Операторы создания - используются для создания нового объекта Observable. Это то, что мы рассмотрели в первом разделе этого поста.
- Конвейерные операторы - используются для преобразования данных в несколько этапов. Они принимают Observable в качестве входных данных и создают Observable в качестве выходных данных. Это чистые функции. Это означает, что предыдущий Observable остается неизменным.
В RxJ доступно множество операторов, но есть несколько, которые вы, возможно, будете использовать чаще.
Вот 7 операторов, которые вы можете использовать и видеть чаще:
- карта
- фильтр
- уменьшать
- concat
- слияние
- mergeMap
- switchMap
Операторы map (), filter () и reduce ()
Если вы в прошлом работали с массивами, эти операторы, вероятно, покажутся вам знакомыми. Они работают точно так же, но в случае с операторами каждый из них возвращает Observable.
Рассмотрим следующие
Операторы concat () и merge ()
… Оба делают одно и то же.
Оба они возвращают Observable, который генерирует значения из каждого входного Observable.
Ключевое различие между ними заключается в том, как каждый из них передает значения. Как будет определять, какой из них вы будете использовать для различных вариантов использования.
concat
объединяет несколько Observable и передает их значения последовательно.
merge
объединяет несколько Observable вместе и передает их значения одновременно.
Оператор mergeMap ()
A.K.A Оператор flatMap()
позволяет нам сгладить вложенный Observable.
Давайте продолжим нашу аналогию с Netflix.
У вас есть несколько сезонов из серии, которые можно наблюдать. В каждом сезоне у вас есть 10 серий, которые также доступны для просмотра. В этом случае 10 эпизодов вложены в Observable в каждом сезоне.
Чтобы посмотреть весь сериал, сначала нужно выбрать первый сезон (подписаться на него). Затем вам нужно выбрать (подписаться) на каждую серию одну за другой и просмотреть ее.
Это то, что нам пришлось бы сделать, если бы мы не использовали оператор mergeMap
. У нас должны быть вложенные подписки, чтобы пройти все шоу.
Рассмотрим следующее (без использования mergeMap
)
Вот ключевой вывод из приведенного выше кода:
/** * Without using mergeMap() we'd need to have nested subscriptions * to get through the entire series. */ const TheLastKingdom$ = getAllEpisodesForThisSeries(); // captures one season at a time TheLastKingdom$.subscribe(seasons => { let currentSeason = ''; seasons.subscribe(episode => { // Goes through all episodes before going to the next season console.log(episode); currentSeason = episode.season; }); console.log(`End of ${currentSeason}...\n`) });
Оператор mergeMap
позволяет нам сгладить вложенный объект Observable, чтобы нам не понадобились вложенные подписки.
По нашей аналогии, mergeMap
позволит нам получить беспрепятственный опыт, когда каждый эпизод будет воспроизводиться один за другим, без необходимости выбирать (подписываться) на следующий сезон.
Все в приведенном выше коде остается прежним. Последняя часть теперь будет выглядеть так при использовании оператора mergeMap
.
const TheLastKingdom$ = getAllEpisodesForThisSeries(); TheLastKingdom$ .mergeMap(seasons => seasons) .subscribe(episode => console.log(episode));
Вот как это описывает официальная документация оператора mergeMap
:
Возвращает Observable, который генерирует элементы на основе применения функции, которую вы предоставляете к каждому элементу, испускаемому исходным Observable, где эта функция возвращает Observable, а затем объединяет полученные Observable и генерирует результаты этого слияния.
Оператор switchMap ()
Все еще смотрите Netflix?
Хороший.
Продолжим аналогию.
Вы переходите на страницу поиска и ищете заголовок, который хотите посмотреть. Приложение отправляет запрос на сервер, чтобы получить подробную информацию об этом заголовке. Вы ждете ответа, но это занимает слишком много времени. Это может быть проблема с сетью на вашей стороне или что-то длится дольше на стороне Netflix.
Как бы то ни было, вы теряете терпение и ищете другое название. Этот сразу же возвращается. Вы читаете описание, и внезапно на экране внезапно появляется название, которое вы искали первым.
Ваш опыт испорчен.
Что случилось? Почему первое название вернулось и появилось, хотя вас это больше не волнует?
Ну, ваш первый запрос так и не был отменен. По какой-то причине на ответ ушло больше времени. И как только он ответил, браузер показал, что произошло.
Чтобы исправить проблемы такого рода, вы должны использовать switchMap
Оператор. Давайте посмотрим, как это будет выглядеть.
Вот как это работает.
Наблюдаемый источник, в данном случае showNames$
, передает название каждого шоу с интервалом в одну секунду. Внутренний наблюдаемый объект getSearchedShowDetails$
имитирует HTTP-запрос, чтобы получить подробную информацию о шоу, добавляя случайную задержку перед возвратом ответа.
Если исходный наблюдаемый изменяется до, что внутренний наблюдаемый объект получит ответ, тогда внутренний наблюдаемый перестанет испускать элементы из более раннего запроса и начнет испускать элементы из нового.
Это означает, что если для ответа getSearchedShowDetails$
требуется больше одной секунды, то подробности этого шоу не будут переданы. И все, что было возвращено меньше или ровно за одну секунду, будет выдано.
Вот как будет выглядеть результат выполнения приведенного выше кода.
Последнее значение, выданное в этом случае, потребует полных трех секунд для ответа. Это потому, что наблюдаемый источник не изменил своего значения за это время.
Примечание. При запуске кода в файле switchMap_operator.js
результат может отличаться из-за случайных задержек в функции getSearchedShowDetails$
.
Заключение
Что ж, это все для этого поста.
Теперь вы должны хорошо понимать
- терминология, используемая в RxJs
- роли, которые играют Наблюдатель, Наблюдаемый, Подписчик и Оператор, и их обязанности
- как создать Observable с помощью различных методов.
- несколько наиболее часто используемых операторов и принципы их работы
В стране RxJ еще многое предстоит сделать, но это должно помочь вам начать работу с тем, что происходит.
До следующего раза, мир ✌️
Обязательно ознакомьтесь со второй частью этого поста, посвященной обработке ошибок.
Ресурсы:
JavaScript на простом английском языке
Понравилась эта статья? Если да, то получите больше похожего контента, подписавшись на наш канал на YouTube в Decoded!