You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 01:02:14