--- Редактировать 4 - Дополнительные ресурсы (2018/09/01)
В недавнем эпизоде приключений в Angular Бен Леш и Уорд Белл обсуждают вопросы о том, как и когда отписаться в компоненте. Обсуждение начинается примерно в 1:05:30.
Уорд упоминает right now there's an awful takeUntil dance that takes a lot of machinery
и Ша Резник упоминаетAngular handles some of the subscriptions like http and routing
.
В ответ Бен упоминает, что сейчас идут обсуждения, позволяющие Observables подключаться к событиям жизненного цикла компонента Angular, а Ward предлагает Observable событий жизненного цикла, на которые компонент может подписаться, чтобы узнать, когда завершать Observables, поддерживаемые как внутреннее состояние компонента.
Тем не менее, сейчас нам в основном нужны решения, так что вот некоторые другие ресурсы.
Рекомендация по takeUntil()
шаблону от члена основной команды RxJ Николаса Джеймисона и правило tslint, чтобы помочь обеспечить его соблюдение. https://ncjamieson.com/avoiding-takeuntil-leaks/
Облегченный пакет npm, который предоставляет оператор Observable, который принимает экземпляр компонента (this
) в качестве параметра и автоматически отписывается во время ngOnDestroy
.
https://github.com/NetanelBasal/ngx-take-until-destroy
Еще один вариант вышеупомянутого с немного лучшей эргономикой, если вы не делаете сборки AOT (но мы все должны делать AOT сейчас).
https://github.com/smnbbrv/ngx-rx-collector
Таможенная директива *ngSubscribe
которая работает как асинхронный канал, но создает встроенный вид в вашем шаблоне, чтобы вы могли ссылаться на значение «развернутый» во всем шаблоне.
https://netbasal.com/diy-subscription-handling-directive-in-angular-c8f6e762697f
В комментарии к блогу Николаса я упоминаю, что чрезмерное использование takeUntil()
может быть признаком того, что ваш компонент пытается сделать слишком много, и что следует учитывать разделение существующих компонентов на компоненты Feature и Presentational . Вы можете тогда| async
заметить Observable из компонента Feature в компонент Input
Presentational, что означает, что подписки нигде не нужны. Подробнее об этом подходе читайте здесь
--- Редактировать 3 - «Официальное» решение (2017/04/09)
Я говорил с Уордом Беллом об этом вопросе в NGConf (я даже показал ему этот ответ, который, по его словам, был правильным), но он сказал мне, что команда разработчиков документации для Angular нашла решение этого вопроса, которое не было опубликовано (хотя они работают над его утверждением). ). Он также сказал мне, что я могу обновить свой SO-ответ следующей официальной рекомендацией.
Решение, которое мы все должны использовать в будущем, заключается в добавлении private ngUnsubscribe = new Subject();
поля ко всем компонентам, к которым .subscribe()
обращаютсяObservable
s в своем коде класса.
Затем мы позвоним this.ngUnsubscribe.next(); this.ngUnsubscribe.complete();
в нашngOnDestroy()
методы.
Секретный соус (как уже отмечалось @metamaker ) - звонить takeUntil(this.ngUnsubscribe)
перед каждым из наших.subscribe()
вызовов, что гарантирует очистку всех подписок при уничтожении компонента.
Пример:
import { Component, OnDestroy, OnInit } from '@angular/core';
// RxJs 6.x+ import paths
import { filter, startWith, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import { BookService } from '../books.service';
@Component({
selector: 'app-books',
templateUrl: './books.component.html'
})
export class BooksComponent implements OnDestroy, OnInit {
private ngUnsubscribe = new Subject();
constructor(private booksService: BookService) { }
ngOnInit() {
this.booksService.getBooks()
.pipe(
startWith([]),
filter(books => books.length > 0),
takeUntil(this.ngUnsubscribe)
)
.subscribe(books => console.log(books));
this.booksService.getArchivedBooks()
.pipe(takeUntil(this.ngUnsubscribe))
.subscribe(archivedBooks => console.log(archivedBooks));
}
ngOnDestroy() {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
}
Примечание: важно добавитьtakeUntil
оператор как последний, чтобы предотвратить утечки с промежуточными наблюдаемыми в цепочке операторов.
--- Изменить 2 (2016/12/28)
Источник 5
В учебнике Angular глава Routing теперь заявляет следующее: «Маршрутизатор управляет наблюдаемыми объектами, которые он предоставляет, и локализует подписки. Подписки очищаются при уничтожении компонента, защищая от утечек памяти, поэтому нам не нужно отписываться от параметры маршрута наблюдаемые. " - Марк Райкок
Вот обсуждение вопросов Github для Angular docs, касающихся Router Observables, где Уорд Белл упоминает, что прояснение всего этого находится в работе.
--- Редактировать 1
Источник 4
В этом видео от NgEurope Роб Вормальд также говорит, что вам не нужно отписываться от Router Observables. Он также упоминает http
службу и ActivatedRoute.params
в этом видео с ноября 2016 года .
--- Оригинальный ответ
TLDR:
Для этого вопроса существует (2) вида Observables
- конечное значение и бесконечное значение.
http
Observables
производят конечные (1) ценности и что - то вроде DOM event listener
Observables
производят бесконечные значения.
Если вы вручную звоните subscribe
(не используя асинхронный канал), то unsubscribe
из бесконечного Observables
.
Не беспокойтесь о конечных , RxJs
позаботятся о них.
Источник 1
Я разыскал ответ Роба Вормальда в Angular's Gitter здесь .
Он заявляет (я реорганизован для ясности и акцента мое)
если это однозначная последовательность (например, HTTP-запрос), ручная очистка не нужна (при условии, что вы подписываетесь в контроллере вручную)
я должен сказать «если это последовательность, которая завершается » (из которых однозначные последовательности, а-ля http, являются одной)
если это бесконечная последовательность , вы должны отписаться, что асинхронный канал делает для вас
Также он упоминает в этом видео на YouTube об Observables, которые they clean up after themselves
... в контексте Observables, которые complete
(например, Promises, которые всегда выполняются, потому что они всегда производят 1 значение и заканчиваются - мы никогда не беспокоились о том, чтобы отписаться от Promises, чтобы убедиться, что они очищают xhr
событие слушатели, верно?).
Источник 2
Также в путеводителе по Rangle Angular 2 написано
В большинстве случаев нам не нужно явно вызывать метод отказа от подписки, если мы не хотим отменить досрочно, или если наш Observable имеет более длительный срок службы, чем наша подписка. Поведение операторов Observable по умолчанию заключается в удалении подписки сразу после публикации сообщений .complete () или .error (). Имейте в виду, что RxJS был разработан для того, чтобы использовать его в режиме «забей и забудь» большую часть времени.
Когда делает фразу our Observable has a longer lifespan than our subscription
?
Он применяется, когда подписка создается внутри компонента, который уничтожается до (или незадолго до) Observable
завершения.
Я читаю это как значение, если мы подписываемся на http
запрос или наблюдаемое, которое испускает 10 значений, и наш компонент уничтожается до того, как этот http
запрос вернется или 10 значений будут отправлены, мы все еще в порядке!
Когда запрос вернется или, наконец, Observable
будет получено 10-е значение, все будет завершено, и все ресурсы будут очищены.
Источник 3
Если мы посмотрим на этот пример из того же руководства по Rangle, то увидим, что для Subscription
to route.params
требуется значение, unsubscribe()
потому что мы не знаем, когда они params
перестанут меняться (испуская новые значения).
Компонент может быть уничтожен путем перемещения в этом случае, и в этом случае параметры маршрута, вероятно, все еще будут изменяться (они могут технически измениться до конца приложения), а ресурсы, выделенные в подписке, будут по-прежнему выделяться, поскольку не было completion
.
Subscription
чтоhttp-requests
можно игнорировать, так как они звонят толькоonNext
один раз, а затем они звонятonComplete
.Router
Вместо вызововonNext
несколько раз и никогда не может назватьonComplete
(не уверен , что ...). То же самое касаетсяObservable
s отEvent
s. Так что, думаю, так и должно бытьunsubscribed
.