Skip to content
IRC-CodingIRC-Coding
Reactive ProgrammingObservableObserverRxJSпотоки данныхасинхронность

Введение в Reactive Programming

Основы Reactive Programming: зачем нужна реактивность, Observer и Observable, callbacks, операторы и примеры.

S

schutzgeist

4 min read

Введение в реактивное программирование

Представь типичное веб-приложение, например интернет-магазин.

  • Пользователь нажимает кнопку (показать детали товара).
  • Приложение отправляет запрос на медленный сервер и должно ждать ответа.
  • Только после получения ответа оно может обновить страницу.

1. Фундаментальная проблема: зачем вообще “реактивное”?

Классический (императивный) подход: приложение блокирует выполнение, пока ждёт ответа от сервера. Это неэффективно! Представь ситуацию в ресторане: ты заказываешь блюдо и затем держишь официанта за рукав, пока оно не будет готово. В это время он не может обслуживать других гостей.

Реактивное решение: приложение говорит серверу: “Отправь мне данные, когда они будут готовы. Я не буду здесь ждать, но оставляю тебе свой номер телефона. Позвони мне, когда что-то будет”. Тем временем оно может заниматься другими задачами.

2. Основной принцип: асинхронные потоки данных

Реактивное программирование строится на двух ключевых идеях:

  1. Асинхронность: задачи выполняются “в фоне”. Главная программа не блокируется и не ждёт результата.
  2. Потоки данных (Streams): всё можно рассматривать как поток событий: щелчки мыши, нажатия клавиш, HTTP-запросы, результаты из базы данных и так далее. Эти потоки можно создавать, преобразовывать, фильтровать и комбинировать.

3. Аналогия: модель подписки (газета vs YouTube)

Чтобы понять реактивный способ мышления, сравним два подхода:

Императивный (модель запроса)Реактивный (модель подписки)
АналогияКупить газету в киоскеПодписаться на YouTube-канал
ДействиеТы идёшь в киоск и активно просишь последний номер. (Pull)Ты нажимаешь “Подписаться”. (Push)
В будущемНа следующей неделе ты должен вернуться в киоск, чтобы взять новый номер.Ты получаешь уведомление, как только появляется новое видео.
ПреимуществоТы сразу получаешь то, что хочешь.Ты ничего не пропустишь и не должен постоянно проверять. Контент приходит к тебе.

Реактивное программирование работает по модели подписки (принцип Push). Ты подписываешься на источник данных и получаешь уведомление, как только появляются новые данные.

4. Главные действующие лица: Observer и Observable

Эта абстракция используется почти во всех реактивных фреймворках (например RxJava, Project Reactor).

  • Observable (или Publisher):

    • Кто это? Источник данных. Он производит поток событий или данных.
    • Примеры: кнопка (производит события клика), датчик (производит измерения), ответ сервера.
    • Задача: отслеживать, кто заинтересован в его данных (его “подписчики”).
  • Observer (или Subscriber):

    • Кто это? Потребитель данных. Он заинтересован в данных Observable.
    • Примеры: функция, реагирующая на клик; логика, обрабатывающая значение датчика.
    • Задача: подписаться на Observable. Должен обработать три вида уведомлений.

5. Три callback-функции: как Observer реагирует

Observer должен сообщить Observable, что делать при каждом событии. Это достигается реализацией трёх методов:

  1. onNext(T value)

    • Когда? Вызывается, когда Observable выдаёт новый элемент данных.
    • Что делать? Обработай полученный элемент (например, покажи товары, залогируй значение датчика).
    • Аналогия YouTube: было загружено новое видео.
  2. onError(Throwable error)

    • Когда? Вызывается, если произошла ошибка.
    • Что делать? Обработка ошибки (например, покажи сообщение об ошибке пользователю, попробуй ещё раз).
    • Аналогия YouTube: произошла ошибка при загрузке видео.
  3. onComplete()

    • Когда? Вызывается, когда Observable не будет отправлять больше данных. Поток завершён.
    • Что делать? Очистка ресурсов.
    • Аналогия YouTube: YouTuber удалил свой канал. Новых видео точно не будет.

6. Пример кода (концептуально)

Допустим, мы хотим логировать каждый щелчок мыши на веб-странице.

Императивный подход (псевдокод)

// Мы активно опрашиваем (PULL)
while (true) {
  if (mouseWasClicked()) { // Блокирует, пока не будет клик!
    const clickEvent = getClickEvent(); // Получает клик
    console.log('Click at: ', clickEvent.position);
  }
}
// Проблема: весь цикл блокируется и ждёт только кликов.

Реактивный подход (псевдокод с RxJS)

// Мы подписываемся на источник кликов (PUSH)
fromEvent(document, 'click') // Создаёт Observable из событий клика
  .subscribe(                // Подписываемся и определяем callback-функции
    (clickEvent) => {        // onNext: что происходит при каждом клике
      console.log('Click at: ', clickEvent.clientX, clickEvent.clientY);
    },
    (error) => {             // onError: что происходит при ошибке
      console.error('Something went wrong: ', error);
    },
    () => {                  // onComplete: что происходит в конце (здесь никогда не вызывается, так как клики не заканчиваются)
      console.log('No more clicks. Goodbye!');
    }
  );
// Преимущество: главный поток не блокируется и может выполнять другие задачи.
// Callback-функция вызывается только когда действительно есть событие.

7. Почему это так мощно? Операторы!

Настоящая сила кроется в возможности манипулировать этими потоками данных с помощью операторов, похожих на map, filter, reduce в функциональном программировании.

  • filter: пропускать только определённые события. (Например, только клики внутри конкретного div)
  • map: трансформировать события. (Например, преобразовать событие клика в объект {x: 10, y: 20})
  • debounceTime: “дебаунсить” события. (Например, обработать только последний клик в быстрой последовательности, что отлично для поиск-полей!)
  • merge: объединить несколько потоков. (Например, клики и нажатия клавиш в один общий поток)
fromEvent(document, 'click')
  .pipe(
    filter(event => event.target.id === 'myButton'), // Только клики на #myButton
    debounceTime(250),                               // Подожди 250мс между кликами
    map(event => { return {x: event.clientX, y: event.clientY}; }) // Трансформируй событие
  )
  .subscribe(coord => console.log('Clicked at: ', coord));

Итоги

  • Реактивное программирование – это парадигма для неблокирующей, асинхронной обработки потоков данных.
  • Оно следует модели Push или подписки: потребитель данных (Observer) подписывается на источник данных (Observable).
  • Observer реагирует на три вида событий: новые данные (onNext), ошибка (onError) и завершение (onComplete).
  • Благодаря операторам (таким как filter, map) потоки данных можно элегантно трансформировать и комбинировать.
  • Преимущества: лучшее использование ресурсов (без блокировок), элегантная обработка асинхронных событий, простая композиция сложных операций.
Назад к блогу
Share:

Nächster Artikel in Программирование

Weiterlesen
Algorithmen и структуры данных 2026

Похожие статьи