Javascript.RU

Создать новую тему Ответ
 
Опции темы Искать в теме
  #1 (permalink)  
Старый 03.09.2021, 10:27
Интересующийся
Отправить личное сообщение для Azazaza Посмотреть профиль Найти все сообщения от Azazaza
 
Регистрация: 08.11.2011
Сообщений: 19

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

Я написал функцию, которая вызывается в цикле. В нее передаются 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);

    });
}


Помогите разобраться, в чем проблема.

Последний раз редактировалось Azazaza, 03.09.2021 в 10:33.
Ответить с цитированием
  #2 (permalink)  
Старый 03.09.2021, 10:59
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

А покажите цикл в котором Вы вызываете promiseDoActionChildWindow

(И зачем async в return new Promise(async (... ?)
Ответить с цитированием
  #3 (permalink)  
Старый 04.09.2021, 06:17
Интересующийся
Отправить личное сообщение для Azazaza Посмотреть профиль Найти все сообщения от Azazaza
 
Регистрация: 08.11.2011
Сообщений: 19

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 секунд
        }
    }
Ответить с цитированием
  #4 (permalink)  
Старый 04.09.2021, 08:23
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,492

Непонятно когда вызывается closeChildWindow, ну да ладно, есть очевидная причина: console.log. Консоль точно также хранит ссылку на каждый логированный объект.)
Так если не хотите впустую тратить память - не логируйте ничего жирного, особенно в больших циклах - это перманентная трата памяти до console.clear().)
__________________
29375, 35
Ответить с цитированием
  #5 (permalink)  
Старый 04.09.2021, 15:19
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

Мне кажется, что это проблема не скрипта, а браузера. Они по разному обращаются с памятью. Я провел некоторые тесты, открывающие и закрывающие последовательно 50 окон.
У Chrome действительно занятая память все время возрастает. Спустя некоторое время после последнего окна, он освобождает какую то часть памяти, но не до первоначального значения. Похоже он считает, что раз ему эта память понадобилась разок, то можно ее придержать.

А у Firefox она прыгает, то растет, то уменьшается
Ответить с цитированием
  #6 (permalink)  
Старый 04.09.2021, 15:21
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

Сообщение от Aetae
Непонятно когда вызывается closeChildWindow,
Открытое окно вызывает
window.opener.closeChildWindow(result);
Ответить с цитированием
  #7 (permalink)  
Старый 04.09.2021, 15:34
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

Сообщение от Aetae
Так если не хотите впустую тратить память - не логируйте ничего жирного,
Круто! Действительно в моих тестах после удаления логов память расти перестала.
Ответить с цитированием
  #8 (permalink)  
Старый 04.09.2021, 16:19
Аватар для voraa
Профессор
Отправить личное сообщение для voraa Посмотреть профиль Найти все сообщения от voraa
 
Регистрация: 03.02.2020
Сообщений: 2,704

Ho тем не менее проблемы утечки памяти тут все равно остаются.

Открытое окно делает
window.opener.closeChildWindow(result);

result - объект. Но это объект из окружения этого окна. Он имеет ссылки на другие объекты этого окружения. Хотя бы на Object из этого окружения.
И пока ссылки на него или его части будут существовать в главном окне, все это окружение (или его значительная часть) будет оставаться в памяти.
Тут все зависит от того, какая работа ведется в главном окне. Можно ли корректно скопировать все в своем окружении и очистить эти ссылки.

Если result содержит поля только примитивных типов, или другие объекты с полями примитивных типов, то можно сделать
window.closeChildWindow = function (result)
        {
             result = JSON.parse(JSON.stringify(result))
......
         }

Последний раз редактировалось voraa, 04.09.2021 в 16:31.
Ответить с цитированием
  #9 (permalink)  
Старый 04.09.2021, 22:39
Аватар для Aetae
Тлен
Отправить личное сообщение для Aetae Посмотреть профиль Найти все сообщения от Aetae
 
Регистрация: 02.01.2010
Сообщений: 6,492

Кстати в последние версии браузеров WeakRef подъехал, осталось консоль научить с ним прозрачно работать и совесем хорошо будет.)
__________________
29375, 35
Ответить с цитированием
  #10 (permalink)  
Старый 06.09.2021, 23:50
Интересующийся
Отправить личное сообщение для Azazaza Посмотреть профиль Найти все сообщения от Azazaza
 
Регистрация: 08.11.2011
Сообщений: 19

Ура проблема решена))))
Aetae, благодарю за наводку про console.log.
voraa, огромное спасибо за активную помощь.

Сделал именно так:

Сообщение от voraa Посмотреть сообщение
window.closeChildWindow = function (result)
        {
             result = JSON.parse(JSON.stringify(result))
......
         }
Ответить с цитированием
Ответ



Опции темы Искать в теме
Искать в теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
утечка памяти при создании DOM MadLord Общие вопросы Javascript 2 16.08.2010 12:01
Access is denied при работе с iFrame slivka_83 Общие вопросы Javascript 1 30.09.2009 10:05
Проблема при работе с плагином jQuery Map Hilight REp0rtER jQuery 3 29.07.2009 22:10
Переход между окнами ввода при заполнении. Бушка Общие вопросы Javascript 4 25.03.2009 15:40
"Подарок" от IE при работе с getElementById Snipe Internet Explorer 1 10.11.2008 18:18