Как полностью отсоединить / уничтожить MutationObserver (возможная утечка памяти)

#javascript #events #memory-leaks #garbage-collection #mutation-observers

#javascript #Мероприятия #утечки памяти #сборка мусора #мутация-наблюдатели

Вопрос:

Итак, у меня есть элемент (назовем это «Список»), который я отслеживаю на предмет изменений (добавление / удаление attr и дочернего списка) с помощью MutationObserver. У этого элемента есть «переключатель«, который я использую для включения / отключения взаимодействия с элементами и наблюдения.

Элемент toggle связан с observer.disconnect(), который я использую для отключения наблюдателя, точно так же, как я не хочу дальнейших наблюдений, когда он переключен «ВЫКЛЮЧЕН«.

Теперь проблема возникает, когда я хочу снова взаимодействовать и «наблюдать» за этим элементом. Когда я просматриваю журналы, кажется, что после повторного подключения (observer.connect(…)). Предыдущие мутации все еще хранятся в памяти.

Мне нужно правильно отсоединить этот observer, поскольку я также отправляю POST-запрос на сервер mongodb. Из-за этой проблемы в моем списке мутаций возникают какие-то раздутия, и мой POST-запрос загрязняется ненужными HTTP-запросами. Это не включено в этот пост, но упоминается просто для контекста.


Переходя к фактическому коду, пожалуйста, ознакомьтесь с журналами и упрощенной структурой кода моего приложения:

HTML

 <div class="list" data-observe="false"> <!-- eventListener attached here, e.g, to ".list" -->
    <div class="list-header">
        <div class="list-toggle-wrap">
            <button class="toggle-btn">Toggle</button>
        </div>
    </div>
    <div class="list-content">
        <!-- observed elements here... -->
    </div>
</div>
  

JS

 const watch = e => {
    if (e.target.matches('.toggle-btn')) {

        const list = e.target.closest('.list')
        const config = {
            attributeFilter: ['value', 'class'],
            attributeOldValue: true,
            childList: true,
            subtree: true,
        }

        const rng = Math.floor(Math.random() * 100)
        const randomHash = () => crypto.randomBytes(6).toString('hex')

        // logging random strings to observe if instance is the same
        console.log(randomHash())

        const callback = mutations => {

            // just logging if mutations are being detected
            console.log(`mutation detected: ${rng}`)

            mutations.forEach(mutation => {
                 // handle mutations
            }
        }

        const observer = new MutationObserver(callback)

        if (list.dataset.observe === 'true') {
            // if observing
            console.log('Observer Disconnected.')
            list.removeEventListener('click', watch)
            observer.disconnect()
            list.dataset.observe = 'false'

        } else if (list.dataset.observe === 'false') {
            // if not observing
            console.log('Observer Connected.')
            list.dataset.observe = 'true'
            observer.observe(list, config)
        }
    }
}

const list = document.querySelector('.list')
list.addEventListener('click', watch)
  

console.log

 /* I'm generating these by clicking toggle ON and OFF without mutating
 * anything else
 * 
 * Also do note that I'm filtering uneeded mutations on my callback so
 * it doesn't show/log here, figured I want to mention that since you
 * may ask if this is just some of the actual mutation happening,
 * which is actually not the case
 */

e520c5769d34           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
21cd886b5760           list.controller.js:250:12
Observer Disconnected. list.controller.js:428:20
mutation detected: 44  list.controller.js:254:16
5781b586160c           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
29c47a6f6ece           list.controller.js:250:12
Observer Disconnected. list.controller.js:428:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
797a9dfa19b2           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
b51b16721df9           list.controller.js:250:12
Observer Disconnected. list.controller.js:428:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
145ba03997a5           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
mutation detected: 15  list.controller.js:254:16
8acb6fabd173           list.controller.js:250:12
Observer Disconnected. list.controller.js:428:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
mutation detected: 15  list.controller.js:254:16
1a9f20e41cb5           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
mutation detected: 15  list.controller.js:254:16
mutation detected: 55  list.controller.js:254:16
f4393ece6540           list.controller.js:250:12
Observer Disconnected. list.controller.js:428:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
mutation detected: 15  list.controller.js:254:16
mutation detected: 55  list.controller.js:254:16
762116b899da           list.controller.js:250:12
Observer Connected...  list.controller.js:462:20
mutation detected: 44  list.controller.js:254:16
mutation detected: 72  list.controller.js:254:16
mutation detected: 52  list.controller.js:254:16
mutation detected: 15  list.controller.js:254:16
mutation detected: 55  list.controller.js:254:16
mutation detected: 6   list.controller.js:254:16
and so on...
  

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


То, что я пробовал до сих пор.

  • removedEventListener и повторно добавляется к '.list' элементу при каждом отключении()
  • null ‘редактировал и deleted observer и создавал экземпляр нового Observer при каждом отключении()
  • подключенный прослушиватель для переключения и toggle.parentNode.removeChild(toggle) для удаления элемента из DOM, надеюсь, также удалив ссылки на него (здесь не повезло), затем повторно создав новый переключатель DOM после.

Был бы очень признателен, если бы кто-нибудь мог выяснить, как это обстоит дело, и как я могу собирать мусор / полностью разыменовывать этот observer, поэтому, когда я включу, это будет так, как если бы был установлен новый Observer без сохранения этих предыдущих записей в памяти.

Редактировать: регистрировать сообщение о подключении / отключении

Комментарии:

1. Похоже, вам может понадобиться observer.takeRecords() очистить очередь после отключения.

2. Да, забыл упомянуть, но я уже пробовал это. takeRecords() просто возвращает пустой массив, поскольку я в основном обработал все изменения в первом экземпляре обратного вызова.

3. О, я вижу, вы создаете новый экземпляр MutationObserver при каждом запуске, так что он никогда не будет таким же, как тот, который вы создали ранее, таким образом, его отсоединение служит нулевой цели. Вам нужно объявить только один из них observer вне watch() области видимости и подключить / отключить его при необходимости.

4. Только что сделал это. Вы правы, экземпляр observer должен быть создан только один раз, и он должен быть вне обратного вызова watch, поскольку его помещение внутрь приведет к созданию экземпляра нового observer для каждого нового события.