ManifestV3 new promise error: The message port closed before a response was received
ManifestV3 new promise error: The message port closed before a response was received
我正在开发一个扩展,它在其内容脚本和后台服务工作者(清单 V3)之间进行大量消息传递,我注意到新的基于 Promise 的 V3 有一个奇怪的问题 APIs,特别是 sendResponse() 函数。
对于需要响应的 API 调用,一切正常。但是如果我不需要响应并且不提供回调函数或使用 promise 的 .then() 方法(或 async/await),则会抛出一个 Promise 错误 - 它说“消息端口在 a 之前关闭已收到回复。"
奇怪的是,调用仍然有效,所以我猜这个错误更像是一个警告。
代码示例:
在内容脚本中,向后台发送消息:
chrome.runtime.sendMessage({ type: 'toggle_setting' })
后台脚本获取消息并执行某些操作,然后在不发送响应的情况下退出:
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
if (message.type === 'toggle-setting') {
//* do whatever it does
}
})
该后台代码引发了上述错误。但是,如果我向其中添加一行并调用不带参数的 sendResponse() 函数,则不会发生错误。
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
sendResponse()
if (message.type === 'toggle-setting') {
//* do whatever it does
}
})
所以这消除了错误消息,但我不太清楚为什么在不需要或不需要响应时有必要这样做。是否有其他方式向基于 Promise 的 V3 API 发送信号,或者现在是否有必要调用 sendResponse(),即使您不需要?
在消息侦听器的末尾添加一个 return true
以允许异步响应
是一个bug in Chrome 99-101,固定在Chrome102.
The reason is that sendMessage is now promisified internally, so we can await
it, but the byproduct is that when we don't specify a callback ourselves, it is added internally anyway in order for the call to return a Promise, which means that since we don't call sendResponse in onMessage, the API will think that it was us who made a mistake of using a callback and not providing a response, and report it as such.
解决方法 1:在 chrome.runtime.onMessage
中调用 sendResponse()
发件人(在问题中是内容脚本):
chrome.runtime.sendMessage('test');
receiver(题中是后台脚本):
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
doSomethingWithoutResponding(msg);
sendResponse();
});
解决方法 2:抑制此特定错误
还修复了 API 在调用之前固有的调用堆栈缺失问题。
// sender (in the question it's the content script)
sendMessage('foo');
// sendMessage('foo').then(res => whatever(res));
// await sendMessage('foo');
function sendMessage(msg) {
const err1 = new Error('Callstack before sendMessage:');
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(msg, res => {
let err2 = chrome.runtime.lastError;
if (!err2 || err2.message.startsWith('The message port closed before')) {
resolve(res);
} else {
err2 = new Error(err2.message);
err2.stack += err1.stack.replace(/^Error:\s*/, '');
reject(err2);
}
});
});
}
我正在开发一个扩展,它在其内容脚本和后台服务工作者(清单 V3)之间进行大量消息传递,我注意到新的基于 Promise 的 V3 有一个奇怪的问题 APIs,特别是 sendResponse() 函数。
对于需要响应的 API 调用,一切正常。但是如果我不需要响应并且不提供回调函数或使用 promise 的 .then() 方法(或 async/await),则会抛出一个 Promise 错误 - 它说“消息端口在 a 之前关闭已收到回复。"
奇怪的是,调用仍然有效,所以我猜这个错误更像是一个警告。
代码示例:
在内容脚本中,向后台发送消息:
chrome.runtime.sendMessage({ type: 'toggle_setting' })
后台脚本获取消息并执行某些操作,然后在不发送响应的情况下退出:
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
if (message.type === 'toggle-setting') {
//* do whatever it does
}
})
该后台代码引发了上述错误。但是,如果我向其中添加一行并调用不带参数的 sendResponse() 函数,则不会发生错误。
chrome.runtime.onMessage.addListener( (message, sender, sendResponse) => {
sendResponse()
if (message.type === 'toggle-setting') {
//* do whatever it does
}
})
所以这消除了错误消息,但我不太清楚为什么在不需要或不需要响应时有必要这样做。是否有其他方式向基于 Promise 的 V3 API 发送信号,或者现在是否有必要调用 sendResponse(),即使您不需要?
在消息侦听器的末尾添加一个 return true
以允许异步响应
是一个bug in Chrome 99-101,固定在Chrome102.
The reason is that sendMessage is now promisified internally, so we can
await
it, but the byproduct is that when we don't specify a callback ourselves, it is added internally anyway in order for the call to return a Promise, which means that since we don't call sendResponse in onMessage, the API will think that it was us who made a mistake of using a callback and not providing a response, and report it as such.
解决方法 1:在 chrome.runtime.onMessage
中调用 sendResponse()发件人(在问题中是内容脚本):
chrome.runtime.sendMessage('test');
receiver(题中是后台脚本):
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => { doSomethingWithoutResponding(msg); sendResponse(); });
解决方法 2:抑制此特定错误
还修复了 API 在调用之前固有的调用堆栈缺失问题。
// sender (in the question it's the content script)
sendMessage('foo');
// sendMessage('foo').then(res => whatever(res));
// await sendMessage('foo');
function sendMessage(msg) {
const err1 = new Error('Callstack before sendMessage:');
return new Promise((resolve, reject) => {
chrome.runtime.sendMessage(msg, res => {
let err2 = chrome.runtime.lastError;
if (!err2 || err2.message.startsWith('The message port closed before')) {
resolve(res);
} else {
err2 = new Error(err2.message);
err2.stack += err1.stack.replace(/^Error:\s*/, '');
reject(err2);
}
});
});
}