Pull to refresh

Comments 55

По-моему вместо всей этой тирады достаточно процитировать определение:
Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной или виртуальной памяти компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные участки памяти, или с ошибками системных служб контроля памяти.

Если выполнение операции даже при том, что в UI человек её отменил, порождает полезный, нужный, результат (например загружает данные в панельку, откуда в следующий раз их покажут без обращения к серверу) — то это не утечка. Если нет (скажем ваше приложение всё равно каждый раз при нажатии на кнопку запрашивает данные с сервера, так как считается, что они ну очень бысто там меняются) — то, разумеется, утечка. И показать как она может сожрать гигабайты памяти будет, в таком приложении, совсем несложно.

Всё. Ну вот совсем всё.

Остальное — это детали…
Если нет (скажем ваше приложение всё равно каждый раз при нажатии на кнопку запрашивает данные с сервера, так как считается, что они ну очень бысто там меняются) — то, разумеется, утечка. И показать как она может сожрать гигабайты памяти будет, в таком приложении, совсем несложно.
Не понятно, Вы про мой пример или нет. Если про мой, то покажите утечку, если это «совсем несложно», как говорите.
Спасибо за детальный разбор статьи и примера!

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

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

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

Сейчас я могу лишь предположить, что вы немного неправильно восприняли основной посыл статьи Почему вам НАДО отписываться от Observable. Основная цель в том, чтобы привлечь внимание к данному явлению и показать некоторые способы решения этой проблемы.

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

И самое главное, takeUntil должен быть в конце:
// Wrong!
pipe(
  takeUntil(read),
  switchMapTo(story)
);

// Ok!
pipe(
  switchMapTo(story),
  takeUntil(read)
);
Соблюдение контракта «подписался и отписался» позволяет нам...
Правило должно быть таким: «подписался на бесконечный стрим — отпишись когда он больше не нужен». Просто и понятно. Всё остальное это ваше личное решение и принято оно может быть в зависимости от конкретной ситуации.
Когда мы работаем с Observable, то у нас нет понимая на уровне типов является ли поток бесконечным или конечным, холодным или горячим. Более того, бывает, что могут произойти ситуации, когда код будет работать неправильно, потому что забыли отпустить старый ресурс.

Поэтому вот такое простое правило и получается: если подписался, то убедись, что отписался, когда это больше не нужно. Тем более, что в RxJS есть множество готовых операторов для этого.
Observable, которые создает HttpClient Angular, т.к. там после прихода ответа или в случае ошибки отписка (complete) происходит автоматически.

Ну это как-бы частный случай Observable, и где гарантии что там ответ конечный?
HttpClient не более чем частный и эксклюзивный случай, ведь в статье вы отметили что речь идет о «Observable RxJS», а не о библиотеке фреймворка.
К тому же завтра реализация HttpClient поменяется на очередной HttpClientClient и гарантий что эти потоки будут закрываться самостоятельно не будет никакой.

К тому же что может быть проще утверждения «Не понимаешь всей реализации, просто сделай отписку». Ибо сейчас люди из лагеря «нафига меня мидл заставляет отписываться постоянно» обчитаются ваших статей и будут всем рассказывать что от потоков отписывать не надо, я видел пример на хабре они сами отписываются и чистятся
Увы, никаких ограничений на конечность не нашёл. Не могли бы подсказать куда нужно смотреть?
Я имею ввиду тот случай, когда сервер реально отвечает безразмерным/бесконечным потоком.
Не в контексте RxJS, а в целом — ага, я уже успел повидать прилично реактивного кода, который отписывается совсем даже не по делу. Например, классика (MobX) — внутри объекта есть какие-то реактивные сайд-эффекты, которые действуют только внутри этого объекта. И тем не менее — рядышком лежит пространный и абсолютно бессмысленный код по отписке и повторной подписке. А этого кода могло бы просто не быть, т.к. время жизни реакций в любом случае не будет превышать времени жизни самого объекта, в котором они находятся.
зато вы избегаете трудноотслеживаемых эффектов, которые возможны если бы отписки не было.
Да, не всегда это будет и если писать все правильно, то все будет нормально, но нет гарантии что однажды вам не придется потратить кучу времени на дебаг, которого можно было бы избежать следя за чистотой подписок.
зато вы избегаете трудноотслеживаемых эффектов, которые возможны если бы отписки не было

Очень смешно. Какие еще «трудноотслеживаемые эффекты» в подписках, которые прям в этом же коде и объявлены?
Я б еще понял, если б речь шла про присылаемое через DI и объявленное хрен знает где.
поток может иметь сайд эффекты и наоборот, дополнительные источники далеко от местного кода.
В MobX не потоки, а состояния.
Но в любом случае, это абсолютно бессмысленное метание аргументами — я вам сразу скажу, что в JS (да и во многих других языках тоже) я вам каким-то посторонним кодом при желании сломаю ваш код без каких-либо проблем. И не важно, что там у вас — потоки, не потоки, браузер, не браузер, неважно.

Нет, вывод из вышенаписанного — вовсе не «а давайте защищаться всеми возможными и невозможными способами от вероятностей того, что кто-то со стороны сломает наш код».
А покажите ссылку на этот кусок в MobX? Я думаю, что тогда предметнее получится пообсуждать его — да и у разработчиков спросить, почему так — вдруг им видны исключительные ситуации, которые не видны нам.
А покажите ссылку на этот кусок в MobX?

Как я вам её покажу — это корпоративный код.
Или вы как-то ухитрились прочитать мои слова так, что решили, что в самой MobX есть проблемы?

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

В приложениях с активным использованием rx вы не всегда можете это понять с первого взгляда.
Всегда. 1. Логика. 2. Временно добавить в код проверку:
...
.subscribe({
    next: (res) => {
        ...
    },
    error: (error) => {
        setTimeout(() => {
            console.log(subscription.closed ? 'ERROR - SUBSCRIPTION IS CLOSED' : 'ERROR - SUBSCRIPTION IS NOT CLOSED!');
        }, 0);
    },
    complete: () => {
        console.log('COMPLETED');
    }
});
Не совсем понимаю, что именно вы хотели показать данным примером.

С точки зрения правильности он абсолютно неверен, потому что когда Observable эмитит error или complete, то поток завершается всегда. И все что можно сделать в этом случае, это попросту создать новый поток или ничего не делать.

Исходя из этого, в данном примере мало того, что проверка попросту лишена смысла, так еще и загромождает код, его становится тяжелее писать, читать, понимать и поддерживать. Гораздо более проще было бы, если бы просто отписались.
Хотел показать как увидеть завершение потока.
Я так понимаю, это просто отладочный прием, а не код, который пойдет в продакшн?
Да, я же написал «временно добавить». Это и есть — снять метрики, если в чём-то не уверены, в соответствии с советами Дональда Кнута. Вы с ним не согласны? Если в чём-то не уверены, всегда пишите дополнительный код без проверки?
Ну, если это отладочный прием, значит вы его добавите в свой код, а потом удалите и положите код в репозиторий. Хорошо. Вот у вас ситуация изменилась и вы опять не понимаете (или забыли, или этот код написал ваш коллега) — поток кончается или нет. Вы опять добавите этот отладочный код, чтобы проверить. Опять удалите.
По-моему, вы возлагаете на себя довольно тяжелую ношу, подписываясь под обязательством всегда держать в уме, какие из ваших потоков конечные.
И впрямь, анализ логики устройства потока позволяет понять, бесконечный ли он. Однако как этот анализ происходит?

Наружу мы видим имя переменной и ее тип — reports$: Observable\<DailyReport\> — непонятно, конечный reports или нет. Залезем внутрь, продеремся через цепочки forkJoin'ов, iif'ов и прочих combineLatest' ов (все это — внутри ngrx'овой экосистемы), определим все зависимости нашего reports и наконец найдем — ага, один из потоков, из которого был сформирован $reports — бесконечный!
Ну что ж, значит, надо отписываться.
Или наоборот — вы посмотрели на все потоки, из которых порожден reports и поняли, что reports — конечный. Вот вы и не отписываетесь — незачем же. А как долго он будет оставаться конечным в процессе разработки? Не может ли случиться так, что один из порождающих потоков будет заменен на другой — бесконечный — и вы забудете, что надо бы теперь reports отписываться?
Вот и получается cost-benefit-анализ: сколько нам стоит отписаться от того, что сейчас и так конечное против сколько нам будет стоить не отписаться от того, что было конечным, а стало бесконечным. Я утверждаю, что в этом случае перестраховаться и отписаться будет дешевле во всех смыслах.
Если вы в своем пайпе не делаете никакой подписки на события DOM элементов и не добавляете повторы, то он будет конечным. Вы должны держать под контролем свой код. А если вы делаете в своем пайпе всякие мержи, где комбинируете конечные и бесконечные подписки, скорее всего вам стоит задуматься о читаемости и простоте поддержки вашего кода. Почему вы так уверены, что написали не говнокод? Просто потому что это RxJS?
Я не понял этого тезиса. Вы думаете, что комбинировать конечные и бесконечные потоки — это какая-то редкость? вот товарищ Xuxicheta постом ниже привел пример обычного switchMap'а, которым пользуются все, и который возьмет и сделает отписку обязательной.
Я в процессе ответа, пытался вспомнить, что у нас за штуки бывают бесконечными. Навспоминал WebWorker'ы, EventSource, DOM-события, таймеры и разные виды reply'ев. Уверен, что что-то забыл. С вашей философией нужно распечатать полный список и повесить для команды на видном месте — чтобы каждый раз все всё проверяли, а то не дай бог без нужды отпишутся и засорят кодовую базу.
да легко. Сегодня метод возвращает холодный http request, а завтра к нему добавили какой нибудь Map оператор, который переключает наш реквест на другой поток и ой, он уже не завершается.

Или еще вариант, наш поток порожден из какой-то другой подписки, может он и работает один раз, а его teardown логика вызвана не будет. и связи останутся висеть.
Зачем помнить где-что, не проще ли просто отписываться.
«а завтра...» — читаем про преждевременную оптимизацию.
«Или еще вариант, наш поток порожден из какой-то другой подписки...» — читаем про SOLID.
Перед едой принято мыть руки даже если они выглядят чистыми, хотя вовсе не обязательно что на руках есть что-то плохое. Мы можем легко убедиться в безопасности наших рук, стоит лишь взять микроскоп, исследовать пробы, поставить эксперимент, это несложно.

Да и вообще на руках не может быть ничего вредоносного, читаем руководство «как не запачкать руки», и в будущем таких ситуаций может и не быть, читаем «как перестать беспокоиться и не мыть руки»
Ещё раз. Я не говорил, что отписывать и завершать запрос — это всегда плохо. Я наоборот выступаю против этого «всегда». В статье приведен конкретный пример, когда отписка не нужна. Она там реально не нужна и будет лишним кодом. Не согласны? Вот с этим и спорьте. Не надо мне приписывать то, чего я не говорил.
Я вот не согласен именно с тем, что она когда-либо может быть не нужна. Возьмем уж совсем простой случай. Есть у меня компонент, он зависит от сервиса с функцией, возвращающей Observable\<DailyReport\>:

myService.getDailyReport().subscribe((report) => {this.report = report;})

Надо ли от такого отписываться? Мой ответ — надо, посколько тогда мы будем следовать принципам инкапсуляции. Наружу торчит некоторая Observable, как она получилось — мне, как компоненту, вредно знать. Может быть, она получилась просто из http.get, может быть — из EventSource, может быть, ее наколдовал рогатый черт — и меня это не должно волновать. Меня волнует только то, что это Observable, а Observable'ы бывают бесконечными

Вы же нарушаете инкапсуляцию, настаивая на том, что я должен пойти и почитать код функции getDailyReport(), после чего заключить — «а, ну там же просто http.get, не буду отписываться, это лишнее».

Очень хорошо принципам инкапсуляции учит C++, где наружу может торчать только .h-файл, а сама логика будет спрятана в dll-ке. Вот представьте, что код getDailyReport() до безобразия обфусцирован и не подлежит прочтению пока вы находитесь в своем компоненте. Это улучшит код.
В вашем конкретном случае — да — нужно. Разве я говорил, что не нужно всегда? Ещё раз перечитайте предыдущий мой ответ. Я выступаю против фанатизма и подмены понятий. Привел конкретные примеры, а вы из меня лепите какого-то нового «гуру», который учит жизни. Я как раз против такого. Читайте выводы внизу статьи.
Давайте пройдемся по выводам статьи, у меня к ним очень много вопросов.

Я не говорю, что отписываться от подписок RxJS запросов HttpClient не нужно. Я лишь говорю, что бывают случаи когда этого делать не нужно.


На сколько мы можем быть уверены в том, что частные случаи всегда будут неизменяемыми, например «делать отписки не нужно» не превратятся в «делать отписки нужно»?

Сколько потребуется затратить времени на поиск возникшей ошибки, ее описание, локализацию, исправление или полное переписывание всего участка кода?

Будет ли возможность исправить эту проблему, если на уровне архитектуры не закладывалась отмена потоков?

Можете привести примеры таких случаев, когда отписки делать не нужно, свести их к общему правилу, которое можно будет показывать начинающим людям, чтобы быть спокойным за безопасность их кода?

Не нужно подменять понятия. Если вы говорите про утечку памяти, покажите эту утечку. Не ваши бесконечные console.log, а именно утечку. Память в чём изменяется? Время выполнения операции в чём измеряется? Вот это и нужно показать.


Почему обычный console.log не подходит для демонстрации проблемы?

Что мешает взять готовый пример с проблемой и самому попробовать открыть Developer Tools, запустить профилировку и увидеть все собственными глазами?

Если приведенные мной примеры не являются удачными, то давайте сделаем их лучше? Я только за!

Почему вы считаете, что происходит подмена понятий, если описанная в статье проблема решается простым способом — через отписку?

Предвижу возмущенные возгласы подобных «учителей». Они нам обязательно скажут что-то вроде: «ок, утечки памяти нет, но отпиской мы так же отменяем запрос, а значит мы будем уверены, что не будет больше выполняться никакой код после получения ответа от сервера». Во-первых, я не говорю, что отписка это всегда плохо, я лишь говорю, что вы подменяете понятия. Да, то, что после прихода ответа выполнится ещё какая-то бесполезная операция — это плохо, но защититься от реальной утечки памяти можно только отпиской (в данном случае), а защититься от других нежелательных эффектов можно другими способами. Не нужно запугивать читателей и навязывать им свой стиль написания кода.


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

Я не называю своё решение, которое я применил в тестовом приложении, «серебряной пулей». Наоборот, я призываю дать фронтендеру больше свободы. Пусть он сам принимает решение как ему писать свой код. Не нужно его запугивать и навязывать свой стиль разработки.


В чем именно заключается запугивание и ограничение в свободе, о которой вы говорите?

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

И, простите, но вот как раз из-за подобных решений и была написана та статья с примерами: habr.com/ru/post/479732/#comment_21004844. Вы также можете открыть группу Angular в телеграмме и поискать еще подобных изысков, найдете много интересного.

И, к сожалению, от этого никуда не уйти. Людям RxJS дается не так просто, и требуется потратить огромное количество времени, чтобы во всем разобраться. Поэтому самый простой и действенный способ избежать множества проблем — это хотя бы соблюдать контракт: подписался и отписался.

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

Я против фанатизма и преждевременной оптимизации. Этого в последнее время вижу слишком много.


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

Если вам все еще кажется, что это фанатизм, то добро пожаловать в Angular чат. Попробуйте пару дней помочь людям с их вопросами, а потом еще раз посмотреть на всю ситуацию заново.

Я действительно считаю, что это будет отличным опытом. И на данный момент мой опыт говорит о том, что лучше отписываться от потоков, когда они больше не нужны, так как рано или поздно это приводит к проблемам, которые разрешить становится очень сложно.
Давайте пройдемся по выводам статьи, у меня к ним очень много вопросов.
Так не пойдет. Проходиться нужно не по выводам, а по всему тексту статьи. Тогда многие вопросы отпадут сами собой.
Почему обычный console.log не подходит для демонстрации проблемы?
Что мешает взять готовый пример с проблемой и самому попробовать открыть Developer Tools, запустить профилировку и увидеть все собственными глазами?
Я открыл, проверил и оказалось, что ваши размышления не имеют ничего общего с реальностью. Подробности есть в статье.
Если приведенные мной примеры не являются удачными, то давайте сделаем их лучше? Я только за!
Тут я вам не помощник, потому что по моему мнению вы пытаетесь доказать то, чего нет. Точнее называете утечкой то, что ей не является.
лучше отписываться от потоков, когда они больше не нужны, так как рано или поздно это приводит к проблемам
Приведите конкретный пример. Можно даже из реального проекта. Не надо заставлять читателя поверить вам на слово или придумывать пример самостоятельно. Хватит пустословия! Больше конкретики. Покажите код, где ваша методика предугадывания проблем пригодилась.

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


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

Выше вы сказали:
Я открыл, проверил и оказалось, что ваши размышления не имеют ничего общего с реальностью. Подробности есть в статье.


Тут я вам не помощник, потому что по моему мнению вы пытаетесь доказать то, чего нет. Точнее называете утечкой то, что ей не является.


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


По поводу примеров:
Приведите конкретный пример. Можно даже из реального проекта. Не надо заставлять читателя поверить вам на слово или придумывать пример самостоятельно. Хватит пустословия! Больше конкретики. Покажите код, где ваша методика предугадывания проблем пригодилась.


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

Из комментария ниже habr.com/ru/post/479732/?reply_to=21007180#comment_21007262:
Кстати, в статье я привел пример такой же потенциальной проблемы, которая возникает из-за отписки (в случае если нет блокировки закрытия окна). Но вы это игнорируете, даже не пытаетесь возразить что-то конкретное. Это ли на фанатизм?


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

Вы просто поймите одну важную и очевидную вещь: отсутствие отписки может привести к потенциальным проблемам, в то время как существование отписки помогает избегать множества потенциальных проблем, которые могут случиться, и на это не требуется много времени и сил.
В статье вы писали:
И действительно, на его стенде можно увидеть как процессор выполняет ненужную работу (когда я ничего не нажимаю) и как увеличивается расход памяти (изменение небольшое)
А дальше не надо читать? Не вырывайте кусочек и контекста.

Не вы ли говорили, что преждевременная оптимизация это зло?
Это был только пример того, что отписываться нужно не всегда. Я там несколько раз сделал оговорку, что не предлагаю так делать везде и всегда и «на всякий случай».

Что вы пристали к этим отпискам? Разве статья называется «не отписывайтесь»? Нет. Не это главное.
А дальше не надо читать? Не вырывайте кусочек и контекста.


Можете показать, что именно мне необходимо прочитать? Я действительно не вижу, что вы имеете в виду.

Это был только пример того, что отписываться нужно не всегда. Я там несколько раз сделал оговорку, что не предлагаю так делать везде и всегда и «на всякий случай».


Вы правда считаете, что блокировать UI — это действительно хорошая идея и предлагаете делать это всем?
Можете показать, что именно мне необходимо прочитать? Я действительно не вижу, что вы имеете в виду.
Со слов «Т.е. человек перехватил конечный стрим...». Вы изменили логику приложения, поэтому появилась проблема. Это Вы сделали не для какого-то полезного действия внутри программы, а просто для вывода строки в консоль. Тем самым ввели читателя в заблуждение. Существует тысяча способов изменить логику приложения и нарушить его работу. Ещё в районе слов (выше) «Выходит, что „учитель“ был не прав, когда говорил...».

Вы правда считаете… и предлагаете...
Это феноминально. Я пишу «я не предлагаю...», а он мне совсем обратный смысл. Я считаю, что в некоторых случая это вполне нормальное решение. А если вы будете и это за меня решать, то может будете за меня писать код?

Ещё раз. Читаем то, что выделено жирным. Но с вами диалог я заканчиваю. Мы ходим по кругу. Отвечать больше не буду.
Ещё раз. Читаем то, что выделено жирным. Но с вами диалог я заканчиваю. Мы ходим по кругу. Отвечать больше не буду.


Как минимум некрасиво с вашей стороны вот так поступать. Сначала обращаетесь ко мне публично «человек», обвиняете в том, что я не делал, а потом уходите от ответов на вопросы.

Хоть бы извинились сперва, но да ладно, я обид не держу) Если будет желание пообщаться и разобраться, то всегда можете мне написать в телеграмм.
И, простите, но вот как раз из-за подобных решений и была написана та статья с примерами: habr.com/ru/post/479732/#comment_21004844
Это только для проверки. Там, откуда я взял этот код (мой код), даже есть комментарий:
github.com/andchir/test-angular-app/blob/master/my-app/src/app/components/good-modal/good-modal.component.ts#L31
Кстати, в статье я привел пример такой же потенциальной проблемы, которая возникает из-за отписки (в случае если нет блокировки закрытия окна). Но вы это игнорируете, даже не пытаетесь возразить что-то конкретное. Это ли на фанатизм?
Но в отличии от вас, я никого не запугиваю, давая ложную (непроверенную) информацию, я ничего не утаиваю и не говорю, что моему примеру нужно следовать всегда.
Я да, я не мою руки, если знаю, что они чистые (не ходил на улицу, не трогал ничего грязного, уже мыл 5 минут назад и т.д.).
Это не преждевременная оптимизация, это нормальная минимизация рисков. Разработчик забудет. Разработчик всегда что-то забывает — если вы сами не такой, то в вашей команде точно кто-то такой. Напишите unsubscribe, минусы от этого гораздо слабее, чем потенциальный риск от разгребания кода в поисках зависшего потока.
Это не преждевременная оптимизация, это нормальная минимизация рисков.
Согласен, отписка это не преждевременная оптимизация. Я писал не об этом. Читайте обновление #3 внизу статьи.
А вы не подменяете понятий? Причем тут «преждевременная оптимизация» и безопасный код? С кодом работать гораздо проще, когда ты знаешь, что при модификации потока у тебя точно будет происходить отписка, а не сидеть и не изучать исходники либ/сорсов из которых этот поток тянется. Что проще, потратить минуту на написание одной строчки с unsubscribe/switchMap/takeUntil, чем каждый раз смотреть исходники или вставлять ненужный «отладочный» код и проверять его работу?
«Причем тут «преждевременная оптимизация» и безопасный код?»
Не вижу смысла повторять, в статье есть даже дополнение по этой теме. Я не называл отписку преждевременной оптимизацией.
Что проще, потратить минуту на написание одной строчки с unsubscribe/switchMap/takeUntil, чем каждый раз смотреть исходники или вставлять ненужный «отладочный» код и проверять его работу?
На самом деле там всё просто, это же просто JavaScript функции, которые могут быть синхронными и асинхронными. Отписываться от синхронных бессмысленно. Вы же не вызываете «removeEventListener» после того как отфильтровали массив? Там точно такая же логика. Загляните в исходники хотя бы один раз и всё станет понятно.
Это из документации (что происходит при отписке в бесконечном потоке — для понятности переписано):
image
А это из исходников, как создается подписка на событие HTML-элемента (сразу создается функция отписки):
image

В случае асинхронных, конкретно запрос в HttpClient можно в некоторых случаях считать исключением из правил, потому, что поток завершается автоматически. Мне никакие проверки не нужны, проверку я привел для сомневающихся.
Спасибо, но исходники я видел. Мой посыл вы однако не поняли. Зачем мне вообще в них заглядывать, если я использую простой контракт: «подписка = отписка»? Это первое.
Ну и конечно же второе: сегодня у них одна реализация, а завтра они ее меняют. Какой код более устойчив к изменениям реализации того или иного Observable?
Ну и конечно же третий довод: От отписки еще ни одной утечки/нежелательных последствий в коде не было, а вот от их избегания…

И чисто от себя: я всегда делаю отписку, будь то EventListener, паттерн Observable или его реализация ввиде Rxjs. Я даже в сервисах всегда ставлю отписку, потому что сегодня он у меня синглтон, а завтра я начну провайдить его в компоненты и он уже не синглтон.
Читайте обновление #3 внизу статьи. Мне добавить к этому нечего. А если вы с чем-то не согласны, то приводите конкретные цитаты из текста и свои аргументы против.
От одной отписки, перегруженным код не станет(а все подписки внутри сервиса или компонента всегда можно свести к одной строчке отписки). А вот то что он станет более безопасным и менее подверженным ошибкам — это более серьезный довод в эту сторону.
А вот то что он станет более безопасным и менее подверженным ошибкам — это более серьезный довод в эту сторону.
Это вы пишите, основываясь на личной практике, или это теория? У меня тоже есть не малый опыт. Мне сложно представить, чтобы я пришел в какой-то проект и начал бездумно, не глядя, втыкать какой-то свой код. Обычно в команде пишутся юнит-тесты. Они уберегут от ошибок, которые может сделать другой разработчик в вашем коде. Это и есть своего рода стандарт. А то, что говорите вы, стандартом не является. Приходя в какой-то проект, вы уверены, что разработчик, который делал проект до вас, читал те же Best-practice что и вы? Я думаю это маловероятно. А когда вы видите юнит-тесты, вряд ли Вы будете фантазировать. Прошу ответить на вопрос в первом предложении этого комментария. Только честно.
Юнит тесты — видимо у вас очень удачный подбор компаний, потому что я за свою практику встречал всего пару компаний, где фронтенд приложения покрывают юнит тестами.
А по поводу бездумности — как много новичков вы знаете, которые начинают приступать к проекту только после чтения всей документации и просмотра ее исходников? Лично я ни одного такого человека не встречал. Доки обычно смотрятся вскользь, а возвращаются к ним в моменты, когда что-то идет не так, или спустя время, когда менеджер не стоит над душой с новым багом или фичей.
именно так. В контекте ангуляра есть много вещей, где разработчики гарантируют что поток будет завершен при дестрое компонента. А вдруг это не так, вдруг оно изменится, вдруг где-то ошибка?
Я видел проекты которые вели себя весьма странно, где разработчики особо не заморачивались. Рендер кусков убитых компонентов например.
В этом комментарии я приведу два ответа от авторитетных людей в области Angular и RxJS. Выводы делайте сами. Лично мой вывод — RxJS решает одни проблемы и создает новые. Вмеcто того, чтобы расслабиться, ты постоянно в напряжении. У этой библиотеки (методики) есть недостатки (о которых все молчат). Первый человек заботится об облегчении жизни разработчиков, а второй нет. Но второй более авторитетный.

Первый ответ от одного из активных участников команды разработки Angular и NgRx Роба Уормолда:
Вопрос:

Ответ:


Мой перевод:
— В хангаут-разговоре на 27:40 ты говоришь о самоочистке Observables, это происходит всегда?
Необходимо ли нам отписываться, например, в ngOnDestroy в качестве хорошей практики?

— Не видел этот вопрос. Если это последовательность [операция — прим. переводчика] с единичным значением (как http запрос), ручная очистка не обязательна (имея ввиду, что ты подписываешься вручную в контроллере). Если это бесконечная последовательность, ты должен отписаться, что и делает для тебя async pipe.


Второй ответ от разработчика RxJS Бена Леша:

Мой перевод:
— Привет, Бен. Можешь ты мне сказать ВСЕГДА ли мне нужно отписываться от rxjs потоков?
Я не могу найти это в твоих статьях или публичных видео. Спасибо.

— Если это синхронные, или просто несколько значений и это заканчивается (и тебя не волнуют ресурсы), ты можешь забить (если ты лентяй) и не отписываться.

— Но это лучшая практика отписываться.

— Да.


В телеграм чате я обещал человеку под ником Reactive Fox, что если он найдет рекомендацию исполнения контракта «отписался — отпишись» от самого разработчика RxJS, то я принесу ему публичные извинения.
Прошу прощения. Я был не прав, когда говорил, что такой рекомендации в RxJs Нет.
Только плохо, что такой рекомендации нет в документации RxJS.
«отписался — отпишись»
Ошибка. Правильно: «Подписался — отпишись».
Only those users with full accounts are able to leave comments. Log in, please.