Frontend
October 15, 2023

Использование RxJS для работы с асинхронными операциями в Angular

Асинхронное программирование позволяет выполнять несколько задач одновременно, не блокируя работу приложения. Например, приложение может загружать данные с сервера, не замораживая интерфейс. Это делает приложения более отзывчивыми и быстрыми.

Пользователю не приходится ждать загрузки каждой страницы или данных - он может продолжать работать, а задачи выполняются в фоновом режиме. Это очень важно для плавного интерфейса.

Особенно актуально асинхронное программирование для веб-приложений - ведь они постоянно обмениваются данными с сервером. Запросы к API, загрузка контента, обновления страницы - всё это асинхронные операции.

RxJS: Основы

Для работы с асинхронным кодом в JavaScript часто используется библиотека RxJS. Она основана на концепции реактивного программирования и Observable - объектах, которые можно "подписаться" на изменения.

Например, мы можем создать Observable для чтения файла:

const file = Rx.Observable.create(observer => {
  // читаем файл
  observer.next(data); 
});

Затем подписаться на него:

file.subscribe(
  data => {
    // обрабатываем данные
  }  
);

При изменении файла будет вызван наш обработчик данных. Так мы реализуем асинхронную логику.

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

Главные преимущества Observable:

  • Подписка на поток асинхронных данных
  • Композиция и переиспользование потоков
  • Удобные операторы для работы с данными (map, filter, reduce и др.)
  • Обработка ошибок в одном месте
  • Возможность отмены подписки

Благодаря этому, RxJS позволяет писать асинхронный код проще и надежнее.

Рассмотрим более сложный пример. Допустим, нужно выполнить последовательность асинхронных запросов, применяя к результатам преобразования и фильтрацию. С колбэками код будет громоздким и трудно читаемым. А вот реализация с RxJS:

request(url1)
  .pipe(
    map(r => r.json()),
    filter(data => data.value > 5)
  )
  .subscribe(data => {
    // обработка result1
  })

request(url2)
  .pipe(
    map(r => r.json()),
    delay(2000)  
  )
  .subscribe(data => {
    // обработка result2
  })

Код выглядит чисто и понятно. Мы декларативно описываем асинхронные операции when операторами RxJS, не влезая в callback ад.

Использование RxJS в Angular

Во фреймворке Angular RxJS уже встроен и используется во многих местах.

Например, для HTTP запросов есть сервис HttpClient. Он возвращает Observable:

this.http.get('/data')
  .subscribe(
    data => {
      // обработка ответа 
    }
  );

Таким образом запрос выполняется асинхронно. Мы можем выполнять множество HTTP запросов, комбинировать их результаты и применять операторы RxJS.

Другой пример - работа с асинхронными событиями, такими как ввод пользователя, скролл, таймеры и др. Для этого в Angular есть класс Observable:

const clicks = Observable.fromEvent(button, 'click');

clicks.subscribe( () => {
  // обработчик клика 
})

Это позволяет легко реагировать на асинхронные события с помощью RxJS.

Также в Angular есть специальный синтаксис AsyncPipe для удобной подписки в шаблонах:

<div *ngIf="user$ | async as user"> 
  {{ user.name }}
</div>

Здесь переменная user$ - это Observable пользователя. AsyncPipe автоматически подпишется и обновит данные.

В целом RxJS делает код Angular-приложений более элегантным и мощным при работе с асинхронными операциями.

Объяснение примеров

Давай разберем несколько конкретных примеров использования RxJS в Angular подробнее, чтобы лучше понять как это работает.

Вот пример получения данных с сервера:

this.http.get('/api/data')
  .pipe(
    map(response => response.json())  
  )
  .subscribe(
    data => {
      // отображаем данные      
    },
    error => {
      // обрабатываем ошибку
    }
  );

Пошагово разберем что здесь происходит:

  1. Делаем HTTP GET запрос по адресу /api/data с помощью метода get() сервиса HttpClient.
  2. Этот запрос возвращает Observable на который мы можем подписаться.
  3. Применяем оператор map() чтобы преобразовать ответ в JSON.
  4. Подписываемся на Observable с помощью метода subscribe().
  5. В обработчике subscribe получаем результат в переменной data.
  6. Также можем отлавливать ошибки во втором обработчике.

Второй пример - фильтрация событий клавиатуры:

const input = fromEvent(searchInput, 'keyup');

input
  .pipe(
    debounceTime(500),
    filter(event => event.target.value.length > 2),
    map(event => event.target.value)
  )
  .subscribe(query => {
    // выполняем поиск по запросу
  });

Здесь:

  1. Создаём Observable для событий keyup на поле ввода.
  2. Оператор debounceTime() добавляет задержку в 500ms между событиями.
  3. Фильтруем только если длина текста больше 2 символов.
  4. Извлекаем значение поля ввода.
  5. В итоге в subscribe получаем только фильтрованные запросы для поиска.

Таким образом можно гибко обрабатывать и комбинировать события с помощью RxJS.

Польза использования RxJS

Использование RxJS даёт нам много преимуществ:

  • Пишем асинхронный код линейно вместо колбэков ("callback hell")
  • Можно компоновать и повторно использовать потоки данных
  • Удобные операторы для фильтрации, трансформации, объединения потоков
  • Отказоустойчивость - ошибки обрабатываются в одном месте
  • Тестирование и отладка асинхронного кода упрощается
  • Легко описывать сложные асинхронные сценарии декларативно
  • Потоки данных можно отображать в шаблонах (AsyncPipe)
  • RxJS хорошо интегрируется с Angular приложениями

В целом это сильно повышает удобство написания асинхронной логики в Angular. Код становится проще, надежнее и предсказуемее.

Например, мы можем легко реализовать такие сложные сценарии:

  • Параллельно загружать данные из нескольких API
  • Объединять потоки данных из разных источников
  • Делать кеширование HTTP запросов
  • Отменять запросы при навигации пользователя
  • Отслеживать время отклика сервера
  • Повторять запросы при ошибках
  • И многое другое!

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

Итог

Асинхронное программирование очень важно для современных веб-приложений. Библиотека RxJS упрощает работу с асинхронным кодом в Angular, делая его предсказуемым и управляемым.

Я рассказал основы работы с RxJS и привёл несколько примеров его использования в Angular. Надеюсь, это поможет тебе в дальнейшем изучении реактивного программирования и создании крутых приложений!

Удачи в освоении Angular и асинхронного JS!