Ожидание одного обработчика события перед выполнением другого - PullRequest
0 голосов
/ 01 марта 2020

Я пишу расширение для браузера Firefox и застрял в том, как ждать загрузки скрипта контента перед отправкой сообщения из фонового скрипта. Вот последовательность, которую я пытаюсь выполнить:

  1. Пользователь щелкает элемент контекстного меню (обработчик кликов находится в фоновом скрипте)
  2. Фоновый скрипт создает новую вкладку
  3. Скрипт контента полностью загружается в новой вкладке
  4. Фоновый скрипт отправляет сообщение (с данными) в скрипт контента
  5. Скрипт контента использует данные

Очевидно, скрипту контента нужно быть загруженным для шага 4, чтобы работать; в противном случае сообщение не будет получено.

Я посмотрел предыдущие подобные вопросы, но большинство ответов неверны (они оборачивают методы прослушивателя событий в Promise, что приводит либо к слишком большому числу слушателей, либо к слишком большому числу слушателей). несколько обещаний), или они кажутся неприменимыми к моему сценарию (эти ответы полностью решают вопрос, помещая один обратный вызов внутрь другого; это не сработает здесь).

То, что я пробовал до сих пор, было чтобы скрипт содержимого отправлял сообщение, когда он готов, и это работает, но я до сих пор не уверен, как заставить обработчик щелчка (из шага 1) ожидать сообщения из скрипта контента (гипотетический шаг 3.5). Я предполагаю, что мне придется определять обработчик сообщений вне обработчика щелчков, насколько я знаю, если только нет способа получить сообщение внутри обработчика щелчков.

Вот мой текущий код в качестве минимального рабочего примера :

background.js:

let ports = {
    '1': null,
    '2': null
};

xyz = () => { /*...*/ }
tabHasLoaded = () => { /*...*/ }

browser.runtime.onConnect.addListener(connectHandler);
connectHandler = (p) => {
    ports[p.name] = p;
    switch (p.name) {
        case '1':
            ports['1'].addListener(xyz);
            break;
        case '2':
            ports['2'].addListener(tabHasLoaded);
            break;
    }
};

browser.contextMenus.onClicked.addListener((info, tab) => {
    let data, uri;
    //...
    browser.tabs.create({
        url: uri
    }).then((tab) => {
        // need to wait for tabHasLoaded() to get called
        ports['2'].postMessage({
            msg: data
        })
    });
});

1.js (скрипт содержимого для чего-то другого):

let myPort = browser.runtime.connect({
    name: '1'
});
document.addEventListener("click", (e) => {
    myPort.postMessage({
        msg: e.target.id
    });
});

2.js (скрипт контента для новой вкладки, после нажатия контекстного меню):

let myPort = browser.runtime.connect({
    name: '2'
});
myPort.postMessage({
    msg: "READY" // tabHasLoaded() should now get called in background.js
});
myPort.onMessage.addListener((msg) => {
    // waiting for background.js to send me data
});

Есть ли идеальный способ справиться с этим?

1 Ответ

1 голос
/ 01 марта 2020

я все еще думаю, что обещания - это способ go ...

обновить

изменить код для использования вашего MWE ... пожалуйста, обратите внимание, что это непроверенный / неоптимизированный код просто чтобы обрисовать идею ... она должна выглядеть примерно так:

background.js

let ports = {
    '1': null,
    '2': null
};

xyz = () => { /*...*/ }

browser.runtime.onConnect.addListener(connectHandler);
connectHandler = (p) => {
    ports[p.name] = p;
    switch (p.name) {
        case '1':
            ports['1'].addListener(xyz);
            break;
    }
};

browser.contextMenus.onClicked.addListener(async (info, tab) => {
    let data, uri;
    //...
    const tab = await LoadAndWaitForPort2(uri)
    ports['2'].postMessage({msg: data})
});

function LoadAndWaitForPort2(uri){
  return new Promise((resolve, reject)=>{
    const tab
    const tabHasLoaded = (evt) => {
      if(evt.data.msg === "READY"){
        ports['2'].removeListener(tabHasLoaded)
        resolve(tab)
      } else {
        reject("error!")
      }   
    }
    ports['2'].addListener(tabHasLoaded)
    tab = await browser.tabs.create({url: uri})
  })
}

2.js

let myPort = browser.runtime.connect({
    name: '2'
});
myPort.postMessage({
    msg: "READY" // tabHasLoaded() should now get called in background.js
});
myPort.onMessage.addListener((msg) => {
    // waiting for background.js to send me data
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...