В этой статье рассматривается unsubscribe
метод Subject
- и его производные классы - так как он имеет удивительное поведение.
Подписки
Если вы посмотрите на подпись для Observable.prototype.subscribe
, вы увидите, что она возвращает Subscription
. А если вы использовали наблюдаемые объекты, то вы знакомы с вызовом метода unsubscribe
подписки. Однако подписка содержит не только метод unsubscribe
.
В частности, класс Subscription
реализует интерфейс ISubscription
:
Где AnonymousSubscription
- тот же интерфейс, но без свойства только для чтенияclosed
.
Свойство closed
указывает, была ли отписана подписка - вручную или автоматически (если наблюдаемое завершается или возникает ошибка).
Подписчики и отписка
Интересно, что в результате вызова subscribe
возвращается экземпляр класса Subscriber
, который расширяет класс Subscription
.
Подобно методу subscribe
, классу Subscriber
можно передать частичного наблюдателя или отдельные функции обратного вызова next
, error
и complete
.
Основная цель Subscriber
- гарантировать, что методы наблюдателя или функции обратного вызова вызываются только в том случае, если они указаны, и гарантировать, что они не будут вызваны после вызова unsubscribe
или наблюдаемого источника completes
или errors
.
Можно создать Subscriber
экземпляр и передать его в subscribe
вызове, поскольку Subscriber
реализует Observer
интерфейс. Subscriber
будет отслеживать подписки, которые происходят из таких subscribe
вызовов, и unsubscribe
может быть вызван либо для Subscriber
, либо для возвращенного Subscription
.
Также можно передать экземпляр более чем в одном subscribe
вызове, а вызов unsubscribe
на Subscriber
отменит его подписку на все наблюдаемые объекты, на которые он подписан, и пометит его как закрытый. Здесь вызов unsubscribe
отменит подписку как на one
, так и на two
:
Так какое отношение это имеет к предметам? Ну, испытуемые по-разному ведут себя.
Предметы
Субъект одновременно является наблюдателем и наблюдаемым. Класс Subject
расширяет класс Observable
и реализует интерфейс Observer
. Он также реализует интерфейс ISubscription
, поэтому субъекты имеют свойство closed
только для чтения и метод unsubscribe
.
Его реализация ISubscription
предполагает, что - как и в случае с Subscriber
- должна быть возможность подписаться и отписаться от Subject
, например:
Однако произойдет ошибка:
ObjectUnsubscribedError: object unsubscribed at new ObjectUnsubscribedError at Subject.next at SubjectSubscriber.Subscriber._next at SubjectSubscriber.Subscriber.next at MapSubscriber._next at MapSubscriber.Subscriber.next at AsyncAction.IntervalObservable.dispatch at AsyncAction._execute at AsyncAction.execute at AsyncScheduler.flush
Почему? Что ж, метод unsubscribe
в классе Subject
на самом деле ничего не отменяет. Вместо этого он помечает субъект как closed
и устанавливает для своего внутреннего массива подписанных наблюдателей - Subject
extends Observable
, помните - null
.
Субъекты отслеживают наблюдателей, которые подписаны на тему, но в отличие от подписчиков, они не отслеживают наблюдаемые объекты, на которые подписан сам субъект, поэтому субъекты не могут отказаться от подписки на свои источники.
Так почему же возникает ошибка? Ошибка выдается субъектом, когда его метод next
, error
или complete
вызывается после того, как он был помечен как closed
и поведение является преднамеренным:
Если вы хотите, чтобы объект громко и сердито выдавал ошибку, когда вы рядом с ним после того, как он будет полезен, вы можете вызвать функцию отмены подписки непосредственно в самом экземпляре объекта. - Бен Леш
Такое поведение означает, что если вы вызываете unsubscribe
по теме, вы должны быть уверены, что она либо отписана от своих источников, либо что источники завершены или содержат ошибки.
Меры предосторожности
Учитывая такое удивительное поведение, вы можете запретить или предупредить о вызовах unsubscribe
по темам. Если вы это сделаете, мой rxjs-tslint-rules
пакет включает правило, которое делает именно это: rxjs-no-subject-unsubscribe
.
Правило также предотвращает передачу тем в add
метод подписки - метод, который станет темой будущей статьи о составе подписки.
Этот пост также опубликован в моем личном блоге: ncjamieson.com.