Переполнение памяти при работе с дерними окнами
Здравствуйте. У меня задача - пройти по ссылкам на странице, и каждую ссылку открыть в новом дочернем окне, провести в нем определенные действия, вернуть результат выполнения в родительское окно и закрыть дочернее.
Я написал функцию, которая вызывается в цикле. В нее передаются URL из ссылки и дейтсвие, которое необходимо выполнить в дочернем окне. Она открывает новое дочернее окно, и возвращает Promise - ждет пока дочернее окно выполнит свою задачу. /** * Функция выполняет определенное действие на дочерней странице. * Ожидается объект с результатом выполнения * * @param url URL дочерней страницы * @param action Действие. которое требуется произвести * @param timeout Максимальное время ожидание результата * @returns {Promise} */ window.promiseDoActionChildWindow = function (url, action, timeout) { return new Promise(async (resolve, reject) => { // Формирование URL url = new URL(url); url.searchParams.set('helper_action', action); // Открытие нового дочернего окна window.childWindow = window.open(url); // Функция, которая вызывается из дочернего окна после выполнения работ на ней window.closeChildWindow = function (result) { clearTimeout(window.actionTimeout); window.childWindow.close(); if (result.status === 'ok') { resolve(result); } else { reject(result); } } // Принудительное закрытие дочернего окна по таймеру window.actionTimeout = setTimeout(function () { window.childWindow.close(); reject({ status: 'error', message: 'Окно закрыто по таймеру.', url: url, action: action }); }, timeout || 10000); }); } После выполнения действия в дочернем окне - из него вызывается инициализированная в функции promiseDoActionChildWindow() функция closeChildWindow(). window.opener.closeChildWindow(result); Такой способ реализации работает, но на каждой итерации сжирает плюс примерно 30Мб оперативной памяти, а итераций очень много. Я пробовал делать интервал в 10 секунд между итерациями, что бы браузер успевал почистить память, но результат тот-же. Еще безуспешно пробовал обнулять ссылки так: window.promiseDoActionChildWindow= function (url, action, timeout) { return new Promise(async (resolve, reject) => { url = new URL(url); url.searchParams.set('helper_action', action); window.childWindow = window.open(url); window.closeChildWindow = function (result) { window.closeChildWindow = undefined; clearTimeout(window.actionTimeout); window.actionTimeout = null; window.childWindow.close(); window.childWindow = null; if (result.status === 'ok') { resolve(result); } else { reject(result); } } window.actionTimeout = setTimeout(function () { window.closeChildWindow = undefined; window.childWindow.close(); window.childWindow = null; reject({ status: 'error', message: 'Окно закрыто по таймеру.', url: url, action: action }); }, timeout || 10000); }); } Помогите разобраться, в чем проблема. |
А покажите цикл в котором Вы вызываете promiseDoActionChildWindow
(И зачем async в return new Promise(async (... ?) |
voraa, благодарю за внимание. "async" там действительно не к месту, был нужен в свое время, но потом забыл убрать - увяряю Вас, это не причина проблемы, с которой я столкнулся.
Вот сам цикл: async function iterateChannels() { let item = document.querySelector(SELECTOR_CHANNEL + ':nth-child(1)') while (item) { try { let result = await window.promiseDoActionChildWindow(item.href, 'search_id'); item = item.nextSibling; console.log(result.status, result); } catch (result) { console.error(result.status, result); console.log('Повторная попытка.'); } await delay(10000); // Просто задержка на 10 секунд } } |
Непонятно когда вызывается closeChildWindow, ну да ладно, есть очевидная причина: console.log. Консоль точно также хранит ссылку на каждый логированный объект.)
Так если не хотите впустую тратить память - не логируйте ничего жирного, особенно в больших циклах - это перманентная трата памяти до console.clear().) |
Мне кажется, что это проблема не скрипта, а браузера. Они по разному обращаются с памятью. Я провел некоторые тесты, открывающие и закрывающие последовательно 50 окон.
У Chrome действительно занятая память все время возрастает. Спустя некоторое время после последнего окна, он освобождает какую то часть памяти, но не до первоначального значения. Похоже он считает, что раз ему эта память понадобилась разок, то можно ее придержать. А у Firefox она прыгает, то растет, то уменьшается |
Цитата:
window.opener.closeChildWindow(result); |
Цитата:
|
Ho тем не менее проблемы утечки памяти тут все равно остаются.
Открытое окно делает window.opener.closeChildWindow(result); result - объект. Но это объект из окружения этого окна. Он имеет ссылки на другие объекты этого окружения. Хотя бы на Object из этого окружения. И пока ссылки на него или его части будут существовать в главном окне, все это окружение (или его значительная часть) будет оставаться в памяти. Тут все зависит от того, какая работа ведется в главном окне. Можно ли корректно скопировать все в своем окружении и очистить эти ссылки. Если result содержит поля только примитивных типов, или другие объекты с полями примитивных типов, то можно сделать window.closeChildWindow = function (result) { result = JSON.parse(JSON.stringify(result)) ...... } |
Кстати в последние версии браузеров WeakRef подъехал, осталось консоль научить с ним прозрачно работать и совесем хорошо будет.)
|
Ура проблема решена))))
Aetae, благодарю за наводку про console.log. :thanks: voraa, огромное спасибо за активную помощь. :thanks: Сделал именно так: Цитата:
|
Часовой пояс GMT +3, время: 03:39. |