Форум программистов, компьютерный форум, киберфорум
JavaScript: API
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.60/5: Рейтинг темы: голосов - 5, средняя оценка - 4.60
964 / 485 / 241
Регистрация: 02.06.2016
Сообщений: 760
Chrome Extension

Проблема с clearTimeout в расширении chrome

20.03.2019, 18:45. Показов 982. Ответов 3
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Друзья, помогите разобраться с синхронизацией в js. Пишу расширение для chrome, оно состоит из двух скриптов background.js и content.js (он внедряется в страницу). Background последовательно (под одной) окрывает страницы и ждет пока content закончит выполнение. Но могут возникнуть две проблемы:
  1. Сервер не отвечает, тогда chrome не загружает content.js
  2. Оригинальные скрипты на странице могут долго грузиться (итд..) и content.js не может выполнить свою работу или работает долго
поэтому я сделал в background два setTimeout короткий (его handle лежит в longLoading) для решения первой проблемы и длинный (handle в longParsing), если срабатывает один из них, то background закрывает текущее окно и переходит к новой странице (в новом окне).

content.js в свою очередь посылает два сообщения. onPageLoaded - перед началом работы для снятия longLoading. И onParsingFinish - по окончании, для снятия таймера longParsing и перехода к новой странице.

Но через некоторое время начинается рассинхронизация всего этого процесса.. После долгих ковыряний, я стал подозревать, что проблема в clearTimeout.. Я сделал пустой макет расширения (чтобы показать на форуме минимум кода), потом добавил для отладки номера сессий, id'шники таймаутов, логи (и возможно где-то накосячил, тк. не особо силен в js и спешил)... и код стал нечитаемым =( основные функции это: startSession, stopSession, onPageLoaded и onParsingFinish.

Вот в логах видно, что уже после первых сессий снова выполняются удаленые таймауты.. хотя в реальном расширении рассинхрон был минут через 10.
Логи


Я не прошу вас устанавливать расширения, сидеть дебажить итд.. Но может кто-нибудь взглянет намеченным взглядом на исходник и скажет в чем проблема. Может это таймауты, может контент скрипт выполняется после закрытия окна со страницей или еще что.. Основной код в background.js, но прилагаю остальные файлы (чтобы был полный исходник). Заранее спасибо!

В настройках расширения нужно разрешить приватный режим. Запуск командой в консоле:
JavaScript
1
utils.alive = false; utils.startSession();
background.js
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
var utils = new class {
    constructor () {
        this.timeouts = []
        this.session = 0
        this.alive = false // набрать в консоле "utils.alive = false" чтобы завершить цикл
    }
 
    closeAllIncognitoWindows() { 
        console.log(this.session, 'closing all incognito windows')
        return new Promise((resolve, reject) => { 
            chrome.windows.getAll({}, wnds => {
                console.log(this.session, 'founded', wnds.length, 'windows')
                wnds.filter(wnd => wnd.incognito)
                    .reduce((p, wnd) => p.then(_ => this.closeWindow(wnd.id)), Promise.resolve())
                    .then(_ => resolve())
            })
        })
    }
 
    closeWindow(windowId){
        return new Promise((resolve, reject) => {
            chrome.windows.remove(windowId, () => {
                resolve()
            })
        })
    }
 
    delay(a, b = a) {
        return new Promise((resolve, reject) => {
            setTimeout(resolve, Math.random() * (b - a) + a)
        }) 
    }
 
    startSession() {
        this.session++;
        console.log('%cЗапускается сессия #%d', 'color: blue; font-weight: bold; font-size: 1.2em;', this.session)
        return this
            .delay(1000) // какието долгие действия
            .then(() => {
                let localSession = this.session
 
                let localLongLoading = setTimeout(() => { 
                    if (localSession != this.session) {
                        console.error('longLoading: Не совпадают номера сессий %d и %d для таймаута %d', this.session, localSession, localLongLoading)
                        return
                    }
                    console.log(this.session, 'Сервер не отвечает... таймаут: ', localLongLoading)
                    this.stopSession()
                }, 10 * 1000)
                this.timeouts.longLoading = localLongLoading
                console.log('%d setTimeout: longLoading = %d', this.session, this.timeouts.longLoading)
 
                let localLongParsing = setTimeout(() => {
                    if (localSession != this.session) {
                        console.error('longParsing: Не совпадают номера сессий %d и %d для таймаута %d', this.session, localSession, localLongParsing)
                        return
                    }
                    console.log(this.session, 'Контент на странице не грузится... таймаут: ', localLongParsing)
                    this.stopSession()
                }, 20 * 1000)
                this.timeouts.longParsing = localLongParsing
                console.log('%d setTimeout: longParsing = %d', this.session, this.timeouts.longParsing)
                
                return new Promise((resolve, reject) => {
                    chrome.windows.create({
                        url: 'https://www.cyberforum.ru',
                        incognito: true,
                        focused: false
                    }, resolve)}
                )
            })
    }
 
    // Событие: сразу при загрузке contenjs должен запросить номер сессии
    onPageLoaded() {
        console.log(this.session, 'onPageLoaded: Страница загружена')
        if (this.timeouts.longLoading){
            console.log('%d clearTimeout: longLoading = %d', this.session, this.timeouts.longLoading)
            clearTimeout(this.timeouts.longLoading)
            this.timeouts.longLoading = null
        }
        return this.session
    }
 
    // Событие: по оконцании работы contenjs
    onParsingFinish(localSession) {
        if (this.session != localSession) {
            console.error('onParsingFinish: Не совпадают номера сессий %d и %d', this.session, localSession)
            return
        }
        console.log('%c%d onParsingFinish: страница отпарсена!', 'color: green', this.session)
        this.stopSession()
    }
 
    stopSession() {
        console.log(this.session, 'Завершение сессии')
        for (let key in this.timeouts){
            if (this.timeouts[key]) {
                console.log('%d clearTimeout: %s = %d', this.session, key, this.timeouts[key])
                clearTimeout[this.timeouts[key]]
                this.timeouts[key] = null
            }
        }
        this.closeAllIncognitoWindows()
            .then(() => { return this.delay(1000) }) // какая-то долгая работа
            .then(() => { return this.alive ? this.startSession() : Promise.resolve()}) // запуск новой сессии
    }
}
 
// при инициализации расширения
chrome.runtime.onInstalled.addListener(() => {
    console.log("JsExt debug initilized")
});
 
// при получении сообщения от элементов расширения
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
    if (request.method) {
        if (Array.isArray(request.arguments))
             sendResponse(utils[request.method](...request.arguments))
        else sendResponse(utils[request.method](request.arguments))
    }
});
content.js
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
console.log('injected: content.js')
 
function delay(a, b = a) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, Math.random() * (b - a) + a)
    }) 
}
 
if(document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded',afterDOMLoaded);
} else {
    afterDOMLoaded();
}
 
function afterDOMLoaded() {
    console.log('afterDOMLoaded')
    let session;
 
    // имитация того, что сервер не грузится
    //if (Math.random() < 0.5) return;
 
    // получаем сессию
    new Promise((resolve,reject) => {
        chrome.runtime.sendMessage(
            {'method': 'onPageLoaded'}, 
            response => { 
                session = response
                console.log('session: ', session)
                resolve()
            })
    })
    // делаем долгую работу
    .then (() => {return delay(10 * 1000, 20 * 1000)})
    // отправляем сообщение о завершении
    .then(() => {
        console.log('finishing... ')
        chrome.runtime.sendMessage({
            method: 'onParsingFinish',
            arguments: [session]
        });
    })
}
manifest.json
JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "name": "JsExt debug",
  "version": "1.0",
  "manifest_version": 2,
 
  "incognito": "spanning",
 
  "permissions": [
    "tabs",
    "activeTab",
    "<all_urls>"
  ],
 
  "background": {
    "scripts": ["background.js"],
    "persistent": true
  },
 
  "content_scripts": [
    {
      "matches": ["*://www.cyberforum.ru/*"],
      "js": ["content.js"],
      "run_at": "document_start"
    }
  ]
}
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
20.03.2019, 18:45
Ответы с готовыми решениями:

Внешний js в расширении chrome
Объясните мне пожалуйста, как подключить в свое расширение chrome сторонний файл js с набором функций? С сайта типо:...

Глобальные переменные в расширении Chrome
Сделал совсем простое расширение для Chrome: по нажатию комбинации клавиш добавляет URL текущей вкладки в подобие блокнота. Сохраненные URL...

Не срабатывает нажатие кнопки в расширении chrome
Здравствуйте! Расширение для chrome. Код расширения: &lt;script type=&quot;text/javascript&quot; src=&quot;js/jquery.js&quot;&gt;&lt;/script&gt; ...

3
Эксперт JS
6497 / 3908 / 2006
Регистрация: 14.06.2018
Сообщений: 6,781
20.03.2019, 19:43
JavaScript
1
var utils = new class {
Вывих сознания. Можно же просто реализовать статический класс.
Хотя идея интересная. Абсолютно анонимный класс )) А конструктор у него какое имя имеет ?
1
 Аватар для Antiplayer
129 / 112 / 39
Регистрация: 27.09.2012
Сообщений: 305
21.03.2019, 19:08
Aael, у меня воспроизвелись ваши ошибки только если несколько сессий запустить до тех пор, пока предыдущая не закончилась.
И как я понял, проблема в том, что у вас есть уже инициализирован объект utils, а вы постоянно меняете данные внутри, при этом в функциях, вызванных таймаутом используете this, и таким образом вы ссылаетесь на уже измененные другой сессией данные. У вас же специально в коде используется локальная переменная
JavaScript
1
 let localSession = this.session;
Ей и надо пользоваться, сравнение внутри колбека таймаута вида localSession != this.session не имеет смысла. localSession это переменная полученная замыканием, а this.session *ссылается на объект utils.

Прошу простить за терминологию, я никогда не писал в таком стиле (с классами и т.д.).
1
964 / 485 / 241
Регистрация: 02.06.2016
Сообщений: 760
21.03.2019, 23:54  [ТС]
Собственно, код выше не работает из-за квадратных скобок в 100 строке (background.js). В полном моем коде не было этой строки, но я его отрефакторил, чтобы абсолютно все логировалось и мухи с супом не смешивались, он заработал, а в чем проблема была - теперь не узнать. Наверное, тоже какая-то мелкая опечатка, но зато ясно, что никакие подводные камни, связанные с работой таймаутов, событий итд.. в расширениях хрома, здесь ни при чем.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
21.03.2019, 23:54
Помогаю со студенческими работами здесь

Не работает скрипт в расширении для chrome
Есть расширение для chrome. Подключено правильно,т.к работают алерты. Расширение должно менять стили по классу. Сам скрипт смены на js...

Событие onclick кнопки в расширении Google Chrome
Пытаюсь решить простую, казалось бы задачу, ничего не получается. Нужно, чтобы на загруженную страничку добавлялась кнопка, а затем, по...

ClearTimeout
Здравствуйте. Нужно что-то придумать... function first () { var timeout = setTimeout(function () { //code },...

Не работает clearTimeout
Суть вот в чем: есть элемент, с ним можно совершать разные действия: 1. Клик, в этом случае переход на другую страницу 2. Двойной клик,...

Из-за чего может зависнуть вызов функции clearTimeout?
Добрый день. Возникла проблема с кодом, а точнее в одной из функции у меня вызывается clearTimeout, а дальше функция как бы зависает -...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Новые блоги и статьи
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизита табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать контроль заполнения реквизита "ПричинаСписания". . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
Вывод данных в справочнике через динамический список
Maks 01.04.2026
Реализация из решения ниже выполнена на примере нетипового справочника "Спецтехника" разработанного в конфигурации КА2. Задача: вывести данные из ТЧ нетипового документа. . .
Программное заполнения текстового поля в реквизите формы документа
Maks 01.04.2026
Алгоритм из решения ниже реализован на нетиповом документе "ВыдачаОборудованияНаСпецтехнику" разработанного в конфигурации КА2, в дополнении к предыдущему решению. На форме документа создается. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru