Использование observables для передачи значений¶
28.02.2022
Observables обеспечивают поддержку передачи сообщений между частями приложения. Они часто используются в Angular и служат для обработки событий, асинхронного программирования и работы с несколькими значениями.
Паттерн наблюдателя — это паттерн проектирования программного обеспечения, в котором объект, называемый субъектом, ведет список своих зависимых объектов, называемых наблюдателями, и автоматически уведомляет их об изменении состояния. Этот паттерн похож (но не идентичен) на паттерн publish/subscribe.
Observables являются декларативными — то есть вы определяете функцию для публикации значений, но она не выполняется до тех пор, пока на нее не подпишется потребитель. Подписавшийся потребитель получает уведомления до тех пор, пока функция не завершится, или пока он не откажется от подписки.
Observables может передавать несколько значений любого типа — литералы, сообщения или события, в зависимости от контекста. API для получения значений одинаково, независимо от того, синхронно или асинхронно они передаются. Поскольку логика установки и удаления обрабатывается observables, код приложения должен быть озабочен только подпиской на потребление значений и отменой подписки. Будь то поток нажатий клавиш, HTTP-ответ или интервальный таймер, интерфейс для прослушивания значений и прекращения прослушивания одинаков.
Благодаря этим преимуществам observables широко используются в Angular, а также при разработке приложений.
Основные способы использования и термины¶
В качестве издателя вы создаете экземпляр Observable
, который определяет функцию subscriber. Именно эта функция выполняется, когда потребитель вызывает метод subscribe()
. Функция подписчика определяет, как получать или генерировать значения или сообщения для публикации.
Чтобы запустить созданный observables и начать получать уведомления, необходимо вызвать его метод subscribe()
, передав ему observer. Это объект JavaScript, определяющий обработчики получаемых уведомлений. Вызов subscribe()
возвращает объект Subscription
, имеющий метод unsubscribe()
, который вызывается для прекращения получения уведомлений.
Приведем пример, демонстрирующий базовую модель использования, в котором показано, как observables можно использовать для предоставления обновлений геолокации.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Определение наблюдателей¶
Обработчик для получения уведомлений от observables реализует интерфейс Observer
. Он представляет собой объект, определяющий методы обратного вызова для обработки трех типов уведомлений, которые может посылать observables:
Тип уведомления | Подробности |
---|---|
next | Требуется. Обработчик для каждого доставленного значения. Вызывается ноль или более раз после начала выполнения. |
error | Необязательно. Обработчик уведомления об ошибке. Ошибка останавливает выполнение экземпляра observables. |
complete | Необязательно. Обработчик уведомления о завершении выполнения. Отложенные значения могут продолжать доставляться следующему обработчику после завершения выполнения. |
Объект наблюдателя может определять любую комбинацию этих обработчиков. Если обработчик для какого-либо типа уведомления не задан, то наблюдатель игнорирует уведомления этого типа.
Подписка¶
Экземпляр Observable
начинает публиковать значения только тогда, когда на него кто-то подписывается. Подписка осуществляется вызовом метода subscribe()
экземпляра с передачей объекта наблюдателя для получения уведомлений.
Для того чтобы показать, как работает подписка, нам необходимо создать новую observables. Существует конструктор, который используется для создания новых экземпляров, но для иллюстрации мы можем воспользоваться некоторыми методами из библиотеки RxJS, которые создают простые observables часто используемых типов:
Методы RxJS | Подробности |
---|---|
of(...items) | Возвращает экземпляр Observable , который синхронно доставляет значения, указанные в качестве аргументов. |
from(iterable) | Преобразует свой аргумент в экземпляр Observables . Этот метод обычно используется для преобразования массива в observables. |
Приведем пример создания и подписки на простую observables с наблюдателем, который записывает полученное сообщение в консоль:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
В качестве альтернативы метод subscribe()
может принимать определения функций обратного вызова в строке, для обработчиков next
, error
и complete
. Например, следующий вызов subscribe()
аналогичен вызову, задающему предопределенный наблюдатель:
1 2 3 4 5 6 |
|
В любом случае требуется обработчик next
. Обработчики error
и complete
являются необязательными.
Функция next()
может принимать, например, строки сообщений, или объекты событий, числовые значения, или структуры, в зависимости от контекста. В общем случае мы называем данные, публикуемые observables, потоком. Любой тип значений может быть представлен с помощью observables, а сами значения публикуются в виде потока.
Создание observables¶
Для создания потока observables любого типа используйте конструктор Observable
. В качестве аргумента конструктор принимает функцию-подписчика, которая должна запускаться при выполнении метода ubscribe()
наблюдаемой. Функция-подписчик получает объект Observer
и может публиковать значения в метод next()
наблюдателя.
Например, для создания observables, эквивалентных of(1, 2, 3)
, описанным выше, можно поступить следующим образом:
Создание observables с помощью конструктора | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Если пойти еще дальше, то можно создать observables, которые публикуют события. В этом примере функция подписчика определена inline.
Создание с помощью пользовательской функции fromEvent | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Теперь вы можете использовать эту функцию для создания observables, которые публикуют события нажатия клавиш:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Мультикастинг¶
Типичный observables создает новое, независимое исполнение для каждого подписавшегося наблюдателя. Когда один наблюдатель подписывается, observables подключает обработчик события и передает значения этому наблюдателю. Когда на него подписывается второй наблюдатель, наблюдаемый подключает новый обработчик событий и передает значения этому второму наблюдателю в отдельном выполнении.
Иногда вместо того, чтобы начинать независимое выполнение для каждого подписчика, необходимо, чтобы каждый подписчик получал одни и те же значения — даже если значения уже начали выдаваться. Это может произойти, например, с observables кликов на объекте документа.
Многоадресная рассылка — это практика рассылки по списку нескольких подписчиков за одно выполнение. При использовании многоадресных observables вы не регистрируете несколько слушателей на документе, а используете первый слушатель и рассылаете значения каждому подписчику.
При создании observables вы должны определить, как вы хотите использовать эту observables и хотите ли вы передавать ее значения многократно.
Рассмотрим пример, который считает от 1 до 3 с задержкой в одну секунду после каждого выданного числа.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
Обратите внимание, что если подписаться дважды, то будет два отдельных потока, каждый из которых будет выдавать значения каждую секунду. Выглядит это примерно так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Изменение observables для многоадресной рассылки может выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
|
Многоадресные observables требуют немного больше настроек, но они могут быть полезны для некоторых приложений. Позже мы рассмотрим инструменты, упрощающие процесс многоадресной рассылки, позволяющие взять любую observables и сделать ее многоадресной.
Обработка ошибок¶
Поскольку observables выдают значения асинхронно, try/catch не позволяет эффективно перехватывать ошибки. Вместо этого вы обрабатываете ошибки, указывая обратный вызов error
на наблюдателе. Возникновение ошибки также приводит к тому, что observables очищает подписки и прекращает выдачу значений. Observables может либо выдавать значения (вызывая обратный вызов next
), либо завершаться, вызывая обратный вызов complete
или error
.
1 2 3 4 5 6 7 8 |
|
Работа с ошибками (и, в частности, восстановление после ошибки) более подробно рассматривается в следующем разделе.