Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Уте́чка па́мяти (англ. memory leak) — процесс неконтролируемого уменьшения объёма свободной оперативной или виртуальной памяти компьютера, связанный с ошибками в работающих программах, вовремя не освобождающих ненужные участки памяти, или с ошибками системных служб контроля памяти.
Если нет (скажем ваше приложение всё равно каждый раз при нажатии на кнопку запрашивает данные с сервера, так как считается, что они ну очень бысто там меняются) — то, разумеется, утечка. И показать как она может сожрать гигабайты памяти будет, в таком приложении, совсем несложно.Не понятно, Вы про мой пример или нет. Если про мой, то покажите утечку, если это «совсем несложно», как говорите.
// Wrong!
pipe(
takeUntil(read),
switchMapTo(story)
);
// Ok!
pipe(
switchMapTo(story),
takeUntil(read)
);
Соблюдение контракта «подписался и отписался» позволяет нам...Правило должно быть таким: «подписался на бесконечный стрим — отпишись когда он больше не нужен». Просто и понятно. Всё остальное это ваше личное решение и принято оно может быть в зависимости от конкретной ситуации.
Observable, которые создает HttpClient Angular, т.к. там после прихода ответа или в случае ошибки отписка (complete) происходит автоматически.
зато вы избегаете трудноотслеживаемых эффектов, которые возможны если бы отписки не было
Мне кажется, что отписываться от потоков надо просто потому что тогда вам не надо будет волноваться — конечный он или бесконечный. В приложениях с активным использованием 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');
}
});
reports$: Observable\<DailyReport\>
— непонятно, конечный reports
или нет. Залезем внутрь, продеремся через цепочки forkJoin
'ов, iif
'ов и прочих combineLatest
' ов (все это — внутри ngrx'овой экосистемы), определим все зависимости нашего reports
и наконец найдем — ага, один из потоков, из которого был сформирован $reports
— бесконечный!reports
и поняли, что reports
— конечный. Вот вы и не отписываетесь — незачем же. А как долго он будет оставаться конечным в процессе разработки? Не может ли случиться так, что один из порождающих потоков будет заменен на другой — бесконечный — и вы забудете, что надо бы теперь reports
отписываться? Observable\<DailyReport\>
: myService.getDailyReport().subscribe((report) => {this.report = report;})
http.get
, может быть — из EventSource
, может быть, ее наколдовал рогатый черт — и меня это не должно волновать. Меня волнует только то, что это Observable, а Observable'ы бывают бесконечнымиgetDailyReport(),
после чего заключить — «а, ну там же просто http.get, не буду отписываться, это лишнее».getDailyReport()
до безобразия обфусцирован и не подлежит прочтению пока вы находитесь в своем компоненте. Это улучшит код.Я не говорю, что отписываться от подписок RxJS запросов HttpClient не нужно. Я лишь говорю, что бывают случаи когда этого делать не нужно.
Не нужно подменять понятия. Если вы говорите про утечку памяти, покажите эту утечку. Не ваши бесконечные console.log, а именно утечку. Память в чём изменяется? Время выполнения операции в чём измеряется? Вот это и нужно показать.
Предвижу возмущенные возгласы подобных «учителей». Они нам обязательно скажут что-то вроде: «ок, утечки памяти нет, но отпиской мы так же отменяем запрос, а значит мы будем уверены, что не будет больше выполняться никакой код после получения ответа от сервера». Во-первых, я не говорю, что отписка это всегда плохо, я лишь говорю, что вы подменяете понятия. Да, то, что после прихода ответа выполнится ещё какая-то бесполезная операция — это плохо, но защититься от реальной утечки памяти можно только отпиской (в данном случае), а защититься от других нежелательных эффектов можно другими способами. Не нужно запугивать читателей и навязывать им свой стиль написания кода.
Я не называю своё решение, которое я применил в тестовом приложении, «серебряной пулей». Наоборот, я призываю дать фронтендеру больше свободы. Пусть он сам принимает решение как ему писать свой код. Не нужно его запугивать и навязывать свой стиль разработки.
Я против фанатизма и преждевременной оптимизации. Этого в последнее время вижу слишком много.
Давайте пройдемся по выводам статьи, у меня к ним очень много вопросов.Так не пойдет. Проходиться нужно не по выводам, а по всему тексту статьи. Тогда многие вопросы отпадут сами собой.
Почему обычный console.log не подходит для демонстрации проблемы?Я открыл, проверил и оказалось, что ваши размышления не имеют ничего общего с реальностью. Подробности есть в статье.
Что мешает взять готовый пример с проблемой и самому попробовать открыть Developer Tools, запустить профилировку и увидеть все собственными глазами?
Если приведенные мной примеры не являются удачными, то давайте сделаем их лучше? Я только за!Тут я вам не помощник, потому что по моему мнению вы пытаетесь доказать то, чего нет. Точнее называете утечкой то, что ей не является.
лучше отписываться от потоков, когда они больше не нужны, так как рано или поздно это приводит к проблемамПриведите конкретный пример. Можно даже из реального проекта. Не надо заставлять читателя поверить вам на слово или придумывать пример самостоятельно. Хватит пустословия! Больше конкретики. Покажите код, где ваша методика предугадывания проблем пригодилась.
Так не пойдет. Проходиться нужно не по выводам, а по всему тексту статьи. Тогда многие вопросы отпадут сами собой.
Я открыл, проверил и оказалось, что ваши размышления не имеют ничего общего с реальностью. Подробности есть в статье.
Тут я вам не помощник, потому что по моему мнению вы пытаетесь доказать то, чего нет. Точнее называете утечкой то, что ей не является.
И действительно, на его стенде можно увидеть как процессор выполняет ненужную работу (когда я ничего не нажимаю) и как увеличивается расход памяти (изменение небольшое)
Приведите конкретный пример. Можно даже из реального проекта. Не надо заставлять читателя поверить вам на слово или придумывать пример самостоятельно. Хватит пустословия! Больше конкретики. Покажите код, где ваша методика предугадывания проблем пригодилась.
Кстати, в статье я привел пример такой же потенциальной проблемы, которая возникает из-за отписки (в случае если нет блокировки закрытия окна). Но вы это игнорируете, даже не пытаетесь возразить что-то конкретное. Это ли на фанатизм?
В статье вы писали:А дальше не надо читать? Не вырывайте кусочек и контекста.
И действительно, на его стенде можно увидеть как процессор выполняет ненужную работу (когда я ничего не нажимаю) и как увеличивается расход памяти (изменение небольшое)
Не вы ли говорили, что преждевременная оптимизация это зло?Это был только пример того, что отписываться нужно не всегда. Я там несколько раз сделал оговорку, что не предлагаю так делать везде и всегда и «на всякий случай».
А дальше не надо читать? Не вырывайте кусочек и контекста.
Это был только пример того, что отписываться нужно не всегда. Я там несколько раз сделал оговорку, что не предлагаю так делать везде и всегда и «на всякий случай».
Можете показать, что именно мне необходимо прочитать? Я действительно не вижу, что вы имеете в виду.Со слов «Т.е. человек перехватил конечный стрим...». Вы изменили логику приложения, поэтому появилась проблема. Это Вы сделали не для какого-то полезного действия внутри программы, а просто для вывода строки в консоль. Тем самым ввели читателя в заблуждение. Существует тысяча способов изменить логику приложения и нарушить его работу. Ещё в районе слов (выше) «Выходит, что „учитель“ был не прав, когда говорил...».
Вы правда считаете… и предлагаете...Это феноминально. Я пишу «я не предлагаю...», а он мне совсем обратный смысл. Я считаю, что в некоторых случая это вполне нормальное решение. А если вы будете и это за меня решать, то может будете за меня писать код?
Ещё раз. Читаем то, что выделено жирным. Но с вами диалог я заканчиваю. Мы ходим по кругу. Отвечать больше не буду.
И, простите, но вот как раз из-за подобных решений и была написана та статья с примерами: habr.com/ru/post/479732/#comment_21004844Это только для проверки. Там, откуда я взял этот код (мой код), даже есть комментарий:
«Причем тут «преждевременная оптимизация» и безопасный код?»Не вижу смысла повторять, в статье есть даже дополнение по этой теме. Я не называл отписку преждевременной оптимизацией.
Что проще, потратить минуту на написание одной строчки с unsubscribe/switchMap/takeUntil, чем каждый раз смотреть исходники или вставлять ненужный «отладочный» код и проверять его работу?На самом деле там всё просто, это же просто JavaScript функции, которые могут быть синхронными и асинхронными. Отписываться от синхронных бессмысленно. Вы же не вызываете «removeEventListener» после того как отфильтровали массив? Там точно такая же логика. Загляните в исходники хотя бы один раз и всё станет понятно.
А вот то что он станет более безопасным и менее подверженным ошибкам — это более серьезный довод в эту сторону.Это вы пишите, основываясь на личной практике, или это теория? У меня тоже есть не малый опыт. Мне сложно представить, чтобы я пришел в какой-то проект и начал бездумно, не глядя, втыкать какой-то свой код. Обычно в команде пишутся юнит-тесты. Они уберегут от ошибок, которые может сделать другой разработчик в вашем коде. Это и есть своего рода стандарт. А то, что говорите вы, стандартом не является. Приходя в какой-то проект, вы уверены, что разработчик, который делал проект до вас, читал те же Best-practice что и вы? Я думаю это маловероятно. А когда вы видите юнит-тесты, вряд ли Вы будете фантазировать. Прошу ответить на вопрос в первом предложении этого комментария. Только честно.
— В хангаут-разговоре на 27:40 ты говоришь о самоочистке Observables, это происходит всегда?
Необходимо ли нам отписываться, например, в ngOnDestroy в качестве хорошей практики?
— Не видел этот вопрос. Если это последовательность [операция — прим. переводчика] с единичным значением (как http запрос), ручная очистка не обязательна (имея ввиду, что ты подписываешься вручную в контроллере). Если это бесконечная последовательность, ты должен отписаться, что и делает для тебя async pipe.
— Привет, Бен. Можешь ты мне сказать ВСЕГДА ли мне нужно отписываться от rxjs потоков?
Я не могу найти это в твоих статьях или публичных видео. Спасибо.
— Если это синхронные, или просто несколько значений и это заканчивается (и тебя не волнуют ресурсы), ты можешь забить (если ты лентяй) и не отписываться.
— Но это лучшая практика отписываться.
— Да.
Прекратите выдавать за утечку памяти что-то другое