等待一个事件处理程序,然后再执行另一个事件处理程序

时间:2020-03-01 07:35:59

标签: javascript dom-events firefox-webextensions

我正在编写Firefox浏览器扩展程序,并且在如何从后台脚本发送消息之前一直等待内容脚本加载。 这是我要实现的顺序:

  1. 用户点击上下文菜单项(点击处理程序位于后台脚本中)
  2. 背景脚本创建新标签页
  3. 内容脚本会在新标签页中完全加载
  4. 背景脚本将消息(带有数据)发送到内容脚本
  5. 内容脚本使用数据

很显然,需要加载内容脚本才能使步骤4起作用;否则,该消息将不会被接收。

我看过以前的类似问题,但是大多数答案都是错误的(它们将事件侦听器方法包装在Promise中,这会导致侦听器过多或Promises过多),或者它们似乎不适用于我的情况(这些答案通过将一个回调放在另一个回调中来完全解决了这个问题;在这里不起作用)。

到目前为止,我尝试过的工作是让内容脚本准备就绪后发送一条消息,并且可以正常工作,但是我仍然不确定如何让点击处理程序(来自第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 个答案:

答案 0 :(得分:1)

我仍然认为承诺是可行的方式...

更新

更改代码以使用您的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
});
相关问题