Введение

Observables — это мощный и универсальный инструмент для обработки асинхронных потоков данных в JavaScript. Они похожи на обещания, но с несколькими ключевыми отличиями, которые делают их более мощными и гибкими.

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

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

Еще одним преимуществом observables является возможность использования операторов для манипулирования потоком данных и работы с ним. Операторы — это функции, которые могут быть объединены в цепочку для преобразования, фильтрации или объединения наблюдаемых. Это обеспечивает высокую степень гибкости и контроля над потоком данных.

В Angular observables широко используются, а библиотека RxJS предоставляет широкий набор операторов для работы с observables. Они используются для обработки асинхронных данных и событий, таких как пользовательский ввод, HTTP-запросы и ответы и многое другое. Они также играют ключевую роль в обнаружении изменений, позволяя Angular эффективно обновлять представление при изменении базовых данных.

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

Наблюдаемые объекты RxJS

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

import { Observable } from 'rxjs';

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

let myObservable = new Observable(observer => {
  setInterval(() => {
    observer.next("Hello World!");
  }, 1000);
});

Затем вы можете подписаться на наблюдаемые и получать обновления:

myObservable.subscribe(val => console.log(val));

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

<ng-container *ngIf="myObservable | async as myValue">
  {{myValue}}
</ng-container>

После того, как вы настроили наблюдаемые объекты, вы можете начать работать с ними, используя различные операторы. Например, вы можете использовать оператор map для преобразования данных, испускаемых наблюдаемым объектом:

myObservable.pipe(
  map(val => val.toUpperCase())
).subscribe(val => console.log(val));

В службах и компонентах Angular вы можете использовать наблюдаемые объекты для обработки асинхронных данных и событий. Например, вы можете создать наблюдаемую в службе, которая отправляет обновления с сервера, и подписаться на нее в компоненте для отображения данных в вашем шаблоне.

Работа с наблюдаемыми RxJS в Angular

Создание наблюдаемых из различных источников

Существует несколько способов создания наблюдаемых объектов в JavaScript, и многие из них можно использовать в приложениях Angular. Некоторые распространенные источники для создания наблюдаемых включают:

  • Пользовательский ввод: вы можете создать наблюдаемое из событий пользовательского ввода, таких как нажатия клавиш или клики. Например, вы можете создать наблюдаемую, которая выдает значение поля ввода каждый раз, когда пользователь вводит его:
let input = document.getElementById('myInput');
let inputObservable = fromEvent(input, 'keyup').pipe(
  map(event => event.target.value)
);
  • Таймеры: вы можете создавать наблюдаемые, которые выдают значения через заданный интервал или после задержки. Например, вы можете создать наблюдаемую, которая выдает текущее время каждую секунду:
let timerObservable = interval(1000).pipe(map(() => new Date()));
  • HTTP-запросы: вы можете создавать наблюдаемые объекты, которые выдают ответ на HTTP-запрос. Angular предоставляет модуль HttpClient для выполнения HTTP-запросов, который по умолчанию возвращает наблюдаемые данные. Например, вы можете создать наблюдаемую, которая выдает ответ на запрос GET на определенный URL-адрес:
let httpObservable = this.http.get<MyType>('http://example.com/data').pipe(
  tap(value => console.log(value))
);
  • WebSockets: вы можете создавать наблюдаемые объекты, которые отправляют сообщения, полученные через соединение WebSocket. Вы можете использовать класс RxJS WebSocketSubject для создания наблюдаемого объекта, который отправляет сообщения, полученные через соединение WebSocket.
let wsObservable = new WebSocketSubject<MyType>('ws://example.com/ws');
  • Промисы: вы можете создавать наблюдаемые объекты из промисов, используя оператор from или fromPromise.
let promise = fetch('http://example.com/data').then(response => response.json());
let promiseObservable = from(promise);

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

Преобразование наблюдаемых с помощью операторов

Операторы — это функции, которые можно использовать для манипулирования и преобразования данных, испускаемых наблюдаемым объектом. Некоторые общие операторы для преобразования наблюдаемых включают в себя:

map: оператор map используется для преобразования данных, испускаемых наблюдаемой. Он принимает функцию обратного вызова в качестве аргумента, который применяется к каждому значению, испускаемому наблюдаемым, и возвращает новый наблюдаемый, который испускает преобразованные значения. Например, допустим, у вас есть наблюдаемый объект, который генерирует массив пользовательских объектов, и вы хотите преобразовать его в наблюдаемый объект, который генерирует массив полных имен пользователей. Для этого вы можете использовать оператор map.

let userObservable = of([
  { firstName: 'John', lastName: 'Doe' },
  { firstName: 'Jane', lastName: 'Doe' }]
);

let fullNameObservable = userObservable.pipe(
  map(users => users.map(user => `${user.firstName} ${user.lastName}`))
);

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

let myObservable = of(1, 2, 3, 4, 5);
let filteredObservable = myObservable.pipe(
  filter(val => val % 2 === 0)
);

concatMap: оператор concatMap используется для сопоставления каждого значения с внутренним наблюдаемым и объединения результатов. Он похож на оператор flatMap, но сохраняет порядок внутренних наблюдаемых. Например, предположим, что у вас есть наблюдаемый объект, который генерирует массив идентификаторов пользователей, и вы хотите сделать HTTP-запрос для каждого идентификатора пользователя, чтобы получить сведения о пользователе. Для этого вы можете использовать оператор concatMap.

let userIdObservable = of([1, 2, 3, 4]);

let userDetailObservable = userIdObservable.pipe(
  concatMap(userIds => userIds.map(id => this.http.get(`/api/users/${id}`)))
);

mergeMap: оператор mergeMap используется для сопоставления каждого значения с внутренней наблюдаемой величиной и объединения результатов. Он похож на оператор flatMap, но не поддерживает порядок внутренних наблюдаемых. Например, предположим, что у вас есть наблюдаемый объект, который генерирует поток условий поиска, и вы хотите сделать HTTP-запрос для каждого условия поиска и объединить результаты.

let searchObservable = of('Angular', 'React', 'Vue');

let searchResultsObservable = searchObservable.pipe(
  mergeMap(searchTerm => this.http.get(`/api/search?q=${searchTerm}`))
);

switchMap: оператор switchMap используется для сопоставления каждого значения с внутренним наблюдаемым и выдает только самое последнее наблюдаемое значение. Он откажется от предыдущих внутренних наблюдаемых и переключится на последние внутренние наблюдаемые. Например, предположим, что у вас есть наблюдаемый объект, который выдает поток поисковых запросов, и вы хотите сделать HTTP-запрос для каждого поискового запроса, но выдать только результаты самого последнего поискового запроса. Для этого вы можете использовать оператор switchMap.

let searchObservable = fromEvent(searchInput, 'keyup').pipe(
  map((event: any) => event.target.value),
  switchMap(searchTerm => this.http.get(`/api/search?q=${searchTerm}`))
);

Это всего лишь несколько примеров из множества операторов, доступных для преобразования наблюдаемых в RxJS. Объединяя операторы вместе, вы можете создавать сложные и мощные потоки данных, которые можно использовать для обработки асинхронных данных и событий в приложениях Angular.

Объединение наблюдаемых с помощью операторов

В приложениях Angular существует несколько способов объединения наблюдаемых с помощью операторов. Вот некоторые распространенные примеры:

  • Объединение значений нескольких входных данных формы: вы можете создавать наблюдаемые объекты из событий valueChanges нескольких элементов управления формы, а затем использовать оператор combineLatest для объединения значений в один наблюдаемый объект, который создает объект с последними значениями всех элементов управления формы.
let firstNameControl = new FormControl();
let lastNameControl = new FormControl();
let combinedObs = combineLatest(
   firstNameControl.valueChanges,
   lastNameControl.valueChanges
 ).pipe(map(([firstName, lastName]) => ({firstName, lastName})));
  • Объединение результатов нескольких HTTP-запросов: вы можете использовать оператор forkJoin для объединения результатов нескольких HTTP-запросов в один наблюдаемый объект.
let obs1 = this.http.get('/api/data1');
let obs2 = this.http.get('/api/data2');
let combinedObs = forkJoin({data1: obs1, data2: obs2});
  • Объединение нескольких наблюдаемых действий пользователя: вы можете создавать наблюдаемые из различных действий пользователя, таких как клики, нажатия клавиш и т. д., а затем использовать оператор merge, чтобы объединить их в один наблюдаемый.
let clickObservable = fromEvent(document, 'click');
let keyObservable = fromEvent(document, 'keyup');
let combinedObs = merge(clickObservable, keyObservable);

Это всего лишь несколько примеров множества способов объединения наблюдаемых в приложениях Angular. Используя такие операторы, как merge, combineLatest, forkJoin, вы можете создавать сложные и мощные потоки данных, которые можно использовать для обработки асинхронных данных и событий в приложениях Angular.

Обработка ошибок в наблюдаемых

Обработка ошибок в наблюдаемых объектах — важный аспект работы с асинхронными данными в приложениях Angular. Существует несколько способов обработки ошибок в наблюдаемых, в том числе:

  • Использование оператора catchError: Оператор catchError используется для обработки ошибок, возникающих в наблюдаемом объекте. Он принимает функцию обратного вызова в качестве аргумента, который вызывается с ошибкой, и возвращает новый наблюдаемый объект, который выдает либо ошибку, либо резервное значение. Например, вы можете использовать оператор catchError для обработки ошибок, возникающих во время HTTP-запроса:
import { catchError } from 'rxjs/operators';

let httpObservable = this.http.get('/api/data').pipe(
  catchError(error => {
    console.error(error);
    return of([]);
  })
);
  • Использование оператора retry: оператор retry можно использовать для повторной попытки наблюдаемого при возникновении ошибки. Он принимает число в качестве аргумента, указывающего, сколько раз повторять наблюдаемое, прежде чем сдаться. Например, вы можете использовать оператор retry, чтобы несколько раз повторить HTTP-запрос, прежде чем сдаться:
import { retry } from 'rxjs/operators';

let httpObservable = this.http.get('/api/data').pipe(
  retry(3)
);
  • Использование оператора retryWhen: Оператор retryWhen можно использовать для повторной попытки наблюдаемого при возникновении ошибки. Он принимает функцию, которая получает наблюдаемое количество ошибок и возвращает наблюдаемое значение, указывающее, когда следует повторить попытку. Это позволяет более точно контролировать, когда и как повторять наблюдаемое. Например, вы можете использовать оператор retryWhen для повторной попытки HTTP-запроса после определенной задержки:
import { retryWhen, delay } from 'rxjs/operators';

let httpObservable = this.http.get('/api/data').pipe(
  retryWhen(errors => errors.pipe(delay(3000)))
);
  • Использование оператора take: Оператор take можно использовать для ограничения количества выбросов от наблюдаемой. В качестве аргумента он принимает число, указывающее максимальное количество выпусков, которое необходимо выполнить перед отменой подписки. Например, вы можете использовать оператор take, чтобы ограничить количество повторных попыток:
import { take } from 'rxjs/operators';

let httpObservable = this.http.get('/api/data').pipe(
  retryWhen(errors => errors.pipe(delay(3000))),
  take(5)
);

Это всего лишь несколько примеров множества способов обработки ошибок в наблюдаемых в приложениях Angular. Важно правильно обрабатывать ошибки, чтобы убедиться, что ваше приложение может восстанавливаться после них и продолжать работать правильно.

Отписка от наблюдаемых

В приложениях Angular важно отписаться от наблюдаемых, когда они больше не нужны, чтобы предотвратить утечку памяти и повысить производительность приложения. Есть несколько способов отписаться от наблюдаемых в Angular, в том числе:

  • Использование оператора takeUntil: Оператор takeUntil можно использовать для отказа от подписки на наблюдаемое, когда другое наблюдаемое выдает значение. Например, вы можете использовать оператор takeUntil, чтобы отписаться от HTTP-запроса при уничтожении компонента:
import { takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';

@Component({
  // ...
})
export class MyComponent implements OnInit, OnDestroy {
  private ngUnsubscribe = new Subject();

  ngOnInit() {
    this.http.get('/api/data').pipe(
      takeUntil(this.ngUnsubscribe)
    ).subscribe(data => { /* ... */ });
  }

  ngOnDestroy() {
    this.ngUnsubscribe.next();
    this.ngUnsubscribe.complete();
  }
}
  • Использование канала async: канал async — это удобная функция, которую можно использовать для автоматической отмены подписки на наблюдаемый объект при уничтожении компонента. Он часто используется в формах, управляемых шаблонами. Например, вы можете использовать канал async, чтобы отписаться от наблюдаемого объекта, который выдает пользовательские данные:
@Component({
  template: `
    <div *ngIf="userData$ | async as userData">
      {{ userData.name }}
    </div>
  `,
  // ...
})
export class MyComponent implements OnInit {
  userData$: Observable<User>;

  ngOnInit() {
    this.userData$ = this.http.get('/api/users/1');
  }
}
  • Использование оператора take: Оператор take можно использовать для ограничения количества выбросов от наблюдаемого перед отменой подписки. Например, вы можете использовать оператор take, чтобы отписаться от наблюдаемого после первой эмиссии:
import { take } from 'rxjs/operators';

let myObservable = interval(1000).pipe(
  take(1)
);

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

Заключение

В заключение, работа с наблюдаемыми и RxJS в приложениях Angular — это мощный способ обработки асинхронных данных. Наблюдаемые объекты предоставляют способ обработки потоков данных и могут использоваться как в службах, так и в компонентах для обработки данных из внешних источников и взаимодействия с пользователем. Понимая, как создавать наблюдаемые объекты из различных источников, преобразовывать наблюдаемые объекты с помощью операторов и обрабатывать ошибки, вы сможете легко создавать отзывчивые и надежные приложения Angular. Кроме того, важно помнить, что подписку на наблюдаемые объекты следует отменять, когда они больше не нужны, чтобы предотвратить утечку памяти и повысить производительность. Благодаря знаниям и примерам, представленным в этом сообщении в блоге, у вас должно быть четкое представление о том, как работать с наблюдаемыми и RxJS в приложениях Angular.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .

Заинтересованы в масштабировании запуска вашего программного обеспечения? Ознакомьтесь с разделом Схема.