Осваивать что-то новое всегда сложно, особенно когда перед тобой сталкиваются потоки терминов, функций и вовсе не очевидных на первый взгляд концепций. Давайте разбираться на простых примерах, попробуем выстроить мини-план по освоению этой темы. Пока я подумываю о том, чтобы написать цикл статей в подобном ключе на темы, которые подсказывает мне мой опыт общения с новичками в отрасли (при условии, что тема и подача вызовут интерес).
Почему вообще стоит изучать RxJS? Реактивное программирование становится всё более популярным подходом в разработке современных приложений, особенно там, где важны асинхронные данные — пользовательский ввод, работа с веб-сокетами, обработка событий или запросы к API. RxJS активно используется в таких фреймворках, как Angular, где его знание практически обязательно для работы с компонентами, сервисами и потоками данных.
Но даже за пределами строгих требований фреймворков, RxJS — это мощный инструмент, который помогает работать с асинхронностью гораздо удобнее и гибче, чем традиционные подходы с колбэками или промисами. Освоив его, вы не только получите преимущество в обслуживании сложных приложений, но и откроете для себя совершенно новый взгляд на управление данными и событиями.
Проблема многих новичков, впервые сталкивающихся с темой подписок, заключается в обилии тем и нюансов, связанных с этим направлением. Тонны статей, дискуссий, документаций на темы, аля «RxJS mergeMap vs switchMap vs concatMap vs exhaustMap» могут вызвать головную боль уже в первые пятнадцать минут) Потому первый мой совет: ограничьте список тем и осваиваемых инструментов до самого минимума, декомпозиция важна везде и всегда — слона надо есть по частям. Так начинающий учиться шахматам сначала разбирает каждую фигуру в отдельности, запоминает возможности и нюансы, постепенно переходя к глобальной стратегии игры. Поэтому давайте смахнем все фигуры с доски и оставим там только пешку.
Представьте себя подписчиком на журналы или газеты
Основная идея Observables — это наблюдение за событиями и реакция на них. Чтобы сделать это понятнее, давайте сравним Observables с системой подписки на журналы или газеты:
-
У вас есть издательство (Источник данных).
-
Вы хотите получать газеты каждый день (Подписчик).
-
Вы подписываетесь на доставку газет.
-
Пока вы подписаны, вам регулярно доставляют газеты (данные).
Обратите внимание на ключевые моменты:
-
Газеты приходят только после подписки.
-
Вы можете отписаться, и подписки больше не будет.
-
Иногда газеты могут перестать приходить (например, издавать журнал перестали).
Observable в программировании работает по той же схеме:
-
Есть источник данных (например, поток событий, результаты запроса на сервер, пользовательский ввод).
-
Вы (подписчик) хотите следить за этими данными и реагировать на их изменения.
-
Вы «подписываетесь» на источник с помощью subscribe().
-
Пока вы подписаны, вы получаете данные или уведомления от источника.
Переходим к программированию: основа работы Observable
Observable — это объект, который производит данные или уведомления. Данные могут быть любого типа: числа, строки, массивы, результаты HTTP-запросов. И вот тут начинается интересное: традиционно на этом моменте принято показывать примеры в духе
import { Observable } from 'rxjs'; const observable = new Observable<number>(subscriber => { subscriber.next(1); // Отправляем значение 1 subscriber.next(2); // Отправляем значение 2 subscriber.next(3); // Отправляем значение 3 subscriber.complete(); // Сообщаем, что поток завершён }); observable.subscribe({ next: value => console.log(value), // Что делать с каждым значением complete: () => console.log('Поток завершён') // Уведомление о завершении }); // Вывод в консоли: // 1 // 2 // 3 // Поток завершён
Да только мой опыт показывает, что это вообще не работает с неофитами — здесь происходит разом слишком много вещей, которые приходится либо долго объяснять построчно, либо предлагать зажмуриться и обращать внимание только на «основную мысль», игнорируя «прочие несущественные детали». Но мы же договорились, что будем пытаться найти самую маленькую тему, потому я предлагаю ознакомиться с оператором from() и строить обучение от него. Почему так? Да просто потому что он «смахивает с доски» все вопросы, связанные с созданием потоков, выбросом событий, завершением подписок и иже с ними. Возвращаясь к примеру с газетами: мы пока хотим разобраться с тем, как получать нашу любимую газету каждый день, а не с тем, как построить типографию и наладить её работу.
Поэтому договоримся, что для наших базовых экспериментов нам нужно будет только две вещи: оператор from и какая-нибудь коллекция объектов, которую мы будем придумывать на ходу.
Где запускать код?
Если у вас не вызывает вопросов идея поднять локально мусорное приложение для опытов, то можно пойти по этому пути, но я бы предложил обратить внимание на online-инструменты — они в очередной раз избавят нас от лишних деталей окружения. Поискать что-то удобное для себя можно просто в google по запросу «typescript online run».
От себя могу предложить посмотреть на playcode. Здесь нам нужно просто добавить библиотеку rxjs в разделе packages (слева) и можно творить.
Создаем поток чисел
Давайте перефразируем предыдущий пример с Observable:
import { from } from 'rxjs'; const obs = from([1,2,3]); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 1 // 2 // 3
Вот и весь базовый пример, которому, однако, стоит уделить достаточно времени. Пример с типографией здесь не очень уместен в силу простоты происходящего, поэтому давайте переключимся на людей) Бывает в разговоре мы задеваем какую-то очень интересную собеседнику тему и он начинает потоком вываливать на нас свои знания/мнения по этому вопросу (может это офисные сплетни о коллегах, а может лор Warhammer’a, тут уж как повезет). Так и obs держит в себе «знания» о числах 1,2,3, чтобы вывалить их на любого обратившегося. Стоит нам подписаться на obs мы последовательно получаем всю коллекцию элементов. При этом, если к obs подключится другой слушатель, он вновь получит всю коллекцию целиком, все как в жизни, этого парня просто не заткнуть)
import { from } from 'rxjs'; const obs = from([1,2,3]); obs.subscribe(value => console.log(value)); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 1 // 2 // 3 // 1 // 2 // 3
Что дальше?
А дальше начните эксперименты с этим микро-кодом, постепенно расширяя функционал. Меняйте данные и их типы, поработайте, например, с коллекцией string’ов или объектов. Свыкнитесь с мыслью, что все эти подписки — лишь всеядный инструмент, действующий по шаблону, а вовсе не супер-сложный космический шаттл.
Дальше стоит рассмотреть логику pipe() с двумя операторами — map() и filter().
Сам pipe() не стоит особо рассматривать — это просто функция, позволяющая нам изменять поток данных, принимая соответствующие операторы (начнём мы с двух упомянутых).
filter
Тот парень со сплетнями вряд ли будет рассказывать вам сплетни о вас самих, эти истории он прибережёт для другого собеседника, вот вам и идея фильтрации: не всегда мы будем работать с полным потоком данных, иногда нам нужно отсеять не актуальные данные.
import { from,filter } from 'rxjs'; const obs = from([1,2,3]).pipe(filter(n => n > 2)); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 3
filter устанавливает условие для публикации данных, и вновь то же пожелание: экспериментируйте, меняйте типы данных, условия, освойтесь полноценно с идеями фильтрации, чтобы позже приходить к другим операторам с большей осознанностью и пониманием.
Стоит здесь обратить внимание на важный нюанс: pipe может быть включен на всей подписке целиком, либо только на конкретном подписчике.
import { from,filter } from 'rxjs'; const obs = from([1,2,3]).pipe(filter(n => n > 2)); obs.subscribe(value => console.log(value)); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 3 // 3
Здесь сам источник фильтрует данные и никто из подписчиков не может получить нефильтрованный набор — наш сплетник не готов делиться всем, что знает.
import { from,filter } from 'rxjs'; const obs = from([1,2,3]); obs.pipe(filter(n => n > 2)).subscribe(value => console.log(value)); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 3 // 1 // 2 // 3
В этом же примере один из слушателей и знать ничего не хочет про числа меньше двух, при том, что obs честно отдает весь набор.
map
Рассказывая истории, сложно удержаться от того, чтобы что-нибудь приукрасить) Вот тут нам и поможет map, способный изменять поток на лету.
import { from,map } from 'rxjs'; const obs = from([1,2,3]).pipe(map(n => n * 2)); obs.subscribe(value => console.log(value)); // Вывод в консоли: // 2 // 4 // 6
-
map берёт каждое значение (например, число, текст, объект) из потока.
-
Преобразует его по вашему запросу.
-
Передаёт дальше уже изменённую версию данных.
Бесконечный поток данных для экспериментов
Предыдущие шаги помогают нам освоится с механизмами работы rxjs, но, в действительности, не особо похожи на что-то асинхронное — код выполняется мгновенно, нет никаких ожиданий и задержек. Поэтому стоит включить в наш стартовый набор еще одну функцию: interval. Она создает бесконечный поток чисел от 0, выдавая их через заданный промежуток времени в миллисекундах.
Давайте создадим Observable, который каждую секунду будет выдавать новое значение:
import { interval } from 'rxjs'; const timerObservable = interval(1000); // Каждую секунду timerObservable.subscribe(value => { console.log(`Прошла секунда: ${value}`); }); // Вывод в консоли: // Прошла секунда: 0 // Прошла секунда: 1 // Прошла секунда: 2 // Прошла секунда: 3 // Прошла секунда: 4
Этот генератор позволит вам немного расширить базу для экспериментов, наряду упомянутыми выше функциями.
Заключение
Главное, что стоит помнить: освоение таких мощных инструментов, как RxJS, требует времени. И это нормально. Серьёзно, никто не становится экспертом за один день.
Не стремитесь сразу понять всё и применять десятки операторов. Среди них легко потеряться, если пытаться «объять необъятное». Начинайте с самого простого, с тех тем, которые уже умещаются в вашем понимании: экспериментируйте с from()/interval()/filter/map пока не почувствуете, что код в рамках этих тем пишется легко и не вызывает трудностей в чтении, следующие шаги будут не в пример легче.
Помните, что каждую сложную тему можно разбить на простые кусочки. Осознанное постепенно движение — это путь к тому, чтобы однажды RxJS стал для вас не запутанной магией, а удобным и мощным инструментом, который так и хочется использовать.
Не бойтесь делать ошибки, задавать вопросы и возвращаться к простым примерам. Все прогрессируют по-разному, но каждый из нас начинал когда-то с базовых вещей.
В следующих статьях мы разберём, как сделать Observables ещё более мощными с помощью других операторов, таких как switchMap, mergeMap и иже с ними и попробуем понять, как вообще осваивать весь этот огромный список операторов.
ссылка на оригинал статьи https://habr.com/ru/articles/890862/
Добавить комментарий