在执行另一个事件处理程序之前等待一个事件处理程序
Waiting for one event handler before executing another one
我正在编写一个 Firefox 浏览器扩展程序,但我一直在思考如何在从后台脚本发送消息之前等待内容脚本加载。
这是我要实现的顺序:
- 用户点击上下文菜单项(点击处理程序在后台脚本中)
- 后台脚本创建新标签
- 内容脚本在新标签页中完全加载
- 后台脚本向内容脚本发送消息(带数据)
- 内容脚本使用数据
显然,第 4 步需要加载内容脚本;否则,不会收到消息。
看了之前类似的问题,大部分答案都不对(他们把事件监听方法包装在一个Promise中,要么导致监听太多,要么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
});
有没有理想的方法来处理这个问题?
我仍然认为承诺是可行的方法...
更新
更改代码以使用您的 MWE...请注意,这是 untested/not-optimized 代码只是为了概述这个想法...它应该看起来像这样:
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
});
我正在编写一个 Firefox 浏览器扩展程序,但我一直在思考如何在从后台脚本发送消息之前等待内容脚本加载。 这是我要实现的顺序:
- 用户点击上下文菜单项(点击处理程序在后台脚本中)
- 后台脚本创建新标签
- 内容脚本在新标签页中完全加载
- 后台脚本向内容脚本发送消息(带数据)
- 内容脚本使用数据
显然,第 4 步需要加载内容脚本;否则,不会收到消息。
看了之前类似的问题,大部分答案都不对(他们把事件监听方法包装在一个Promise中,要么导致监听太多,要么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
});
有没有理想的方法来处理这个问题?
我仍然认为承诺是可行的方法...
更新
更改代码以使用您的 MWE...请注意,这是 untested/not-optimized 代码只是为了概述这个想法...它应该看起来像这样:
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
});