如何解决chrome.scripting.executeScript注入后发消息的时序问题?
方案1:利用executeScript的func参数等待脚本初始化完成
把内容脚本的初始化逻辑(导入模块、注册消息监听)放到executeScript的func里,用异步函数让executeScript等待所有初始化完成后再返回,从根源上解决时序问题。
修改后台代码:
chrome.contextMenus.onClicked.addListener(async (onClickData, tab) => { await chrome.scripting.executeScript({ target: { tabId: tab!.id! }, type: 'module', func: async () => { // 动态导入消息处理模块 const { handleMessage } = await import('./messageHandlers.js'); // 注册消息监听 chrome.runtime.onMessage.addListener(handleMessage); // 返回任意值,确保executeScript等待此异步函数执行完毕 return 'content script ready'; }, }); // 无需定时器,直接发送消息 await chrome.tabs.sendMessage(tab!.id!, { type: "setActiveEditableElement", }); await handleMenuItemClick(onClickData, tab); });
方案2:带重试机制的消息发送
如果不想改动内容脚本的注入方式,给sendMessage加重试逻辑,当第一次发送失败(接收端不存在)时自动重试几次,避免固定定时器的不确定性。
先写重试工具函数:
async function sendMessageWithRetry(tabId, message, maxRetries = 5, retryDelay = 20) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await chrome.tabs.sendMessage(tabId, message); } catch (err) { // 最后一次尝试仍失败则抛出错误 if (attempt === maxRetries - 1) throw err; // 间隔一段时间后重试 await new Promise(resolve => setTimeout(resolve, retryDelay)); } } }
替换原有的sendMessage调用:
chrome.contextMenus.onClicked.addListener(async (onClickData, tab) => { await chrome.scripting.executeScript({ target: { tabId: tab!.id! }, files: [schedulerScript], }); // 使用重试函数发送消息 await sendMessageWithRetry(tab!.id!, { type: "setActiveEditableElement", }); await handleMenuItemClick(onClickData, tab); });
方案3:改用长连接(Chrome.runtime.connect)
如果存在多轮消息交互,用长连接替代一次性sendMessage更稳定,连接建立后双方可随时通信,无需担心单次消息的时序问题。
后台代码:
chrome.contextMenus.onClicked.addListener(async (onClickData, tab) => { await chrome.scripting.executeScript({ target: { tabId: tab!.id! }, files: [schedulerScript], }); // 建立长连接 const port = chrome.tabs.connect(tab!.id!); // 监听内容脚本的响应 port.onMessage.addListener((response) => { if (response.status === 'done') { port.disconnect(); } }); // 发送指令 port.postMessage({ type: "setActiveEditableElement" }); await handleMenuItemClick(onClickData, tab); });
内容脚本修改:
import { handleMessage } from "./messageHandlers"; // 监听长连接请求 chrome.runtime.onConnect.addListener((port) => { port.onMessage.addListener((message) => { // 复用原有消息处理逻辑,结果通过长连接返回 const result = handleMessage(message); port.postMessage({ status: 'done', data: result }); }); });
内容的提问来源于stack exchange,提问作者John Gordon
相关产品推荐
相关产品推荐

