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

Chrome扩展Service Worker中如何等待变量赋值后执行操作?

解决Chrome扩展Service Worker中命令依赖变量的等待问题

我正在开发一款将语音转文字内容写入Google文档的Chrome扩展,在Service Worker中通过端口消息周期性接收处理后的文本并写入文档,同时处理用户的各类命令(如插入新段落、创建项目符号列表等)。当前使用checkCommand(currCommand)函数处理命令,代码如下:

const checkCommand = (cmd) => {
const commands = {
    'foo command': () => {
        console.log('This command should create a bullet list');
        if(currListType !== null){
        callGoogleAPIScript(myToken, 'createBulletList',currListType);
        formatingMode = false;
        }
    },
    'bar command': () => {
        console.log('This command should create a new paragraph');
        callGoogleAPIScript(myToken, 'newParagraph');
        formatingMode = false;
    },
    
};
if (cmd && commands.hasOwnProperty(cmd)) {
    commands[cmd](); // needed [cmd] because split words
} else {
    console.log('Command dosen't exist');
    formatingMode = false;
}};

需求与问题

需要为命令设置特定属性(比如currListType,用于指定项目符号类型:圆形、数字等),必须等该变量完成赋值后,再执行callGoogleAPIScript(myToken, 'createBulletList',currListType)。

曾尝试用Promise+setTimeout实现等待,但Service Worker中setTimeout不可靠,且明明currListType已被全局正确设置,Promise却出现阻塞。


可行解决方案

方案一:消息驱动(推荐,适配Service Worker事件模型)

放弃轮询,将currListType的赋值与命令执行绑定为事件流,利用Service Worker的事件驱动特性实现同步:

// 维护待执行命令队列
let pendingCommands = [];
let currListType = null;

// 监听列表类型设置消息
chrome.runtime.onMessage.addListener((message) => {
  if (message.type === 'setListType') {
    currListType = message.listType;
    // 检查是否有等待该类型的命令
    const pendingIndex = pendingCommands.findIndex(cmd => cmd.name === 'foo command');
    if (pendingIndex !== -1) {
      // 执行命令
      callGoogleAPIScript(myToken, 'createBulletList', currListType);
      formatingMode = false;
      // 移除队列中的待执行命令
      pendingCommands.splice(pendingIndex, 1);
    }
  }
});

// 修改checkCommand函数
const checkCommand = (cmd) => {
  const commands = {
    'foo command': () => {
      console.log('准备创建项目符号列表');
      if (currListType !== null) {
        callGoogleAPIScript(myToken, 'createBulletList', currListType);
        formatingMode = false;
      } else {
        // 将命令加入待执行队列
        pendingCommands.push({ name: cmd });
        // 设置超时兜底,避免无限等待
        setTimeout(() => {
          const index = pendingCommands.findIndex(c => c.name === cmd);
          if (index !== -1) {
            pendingCommands.splice(index, 1);
            formatingMode = false;
            console.log('等待列表类型超时,取消命令');
          }
        }, 5000); // 5秒超时阈值可按需调整
      }
    },
    'bar command': () => {
      console.log('创建新段落');
      callGoogleAPIScript(myToken, 'newParagraph');
      formatingMode = false;
    },
  };
  if (cmd && commands.hasOwnProperty(cmd)) {
    commands[cmd]();
  } else {
    console.log('命令不存在');
    formatingMode = false;
  }
};

使用时,在设置currListType的地方(如Popup或Content Script)发送消息给Service Worker即可:

// 示例:在Popup中选择列表类型后发送消息
chrome.runtime.sendMessage({ type: 'setListType', listType: 'circle' });

方案二:基于自定义事件的等待(替代不可靠的setTimeout)

通过自定义事件监听变量变化,配合AbortController管理等待逻辑,避免轮询阻塞:

// 等待currListType赋值的函数
function waitUntilListTypeSet() {
  return new Promise((resolve, reject) => {
    const abortController = new AbortController();
    const signal = abortController.signal;

    // 监听自定义事件
    const handleListTypeSet = () => {
      if (currListType !== null) {
        abortController.abort();
        resolve();
      }
    };

    self.addEventListener('listTypeSet', handleListTypeSet);

    // 设置超时兜底
    const timeoutId = setTimeout(() => {
      self.removeEventListener('listTypeSet', handleListTypeSet);
      reject(new Error('等待列表类型超时'));
    }, 5000);

    // 清理资源
    signal.addEventListener('abort', () => {
      clearTimeout(timeoutId);
      self.removeEventListener('listTypeSet', handleListTypeSet);
    });
  });
}

// 修改checkCommand中的foo command处理
const checkCommand = (cmd) => {
  const commands = {
    'foo command': async () => {
      console.log('准备创建项目符号列表');
      try {
        await waitUntilListTypeSet();
        callGoogleAPIScript(myToken, 'createBulletList', currListType);
        formatingMode = false;
      } catch (err) {
        console.log(err.message);
        formatingMode = false;
      }
    },
    'bar command': () => {
      console.log('创建新段落');
      callGoogleAPIScript(myToken, 'newParagraph');
      formatingMode = false;
    },
  };
  if (cmd && commands.hasOwnProperty(cmd)) {
    commands[cmd]();
  } else {
    console.log('命令不存在');
    formatingMode = false;
  }
};

需要在设置currListType的地方触发自定义事件:

// 设置currListType后发送消息触发事件
chrome.runtime.sendMessage({ type: 'setListType', listType: 'number' }, () => {
  chrome.runtime.sendMessage({ event: 'listTypeSet' });
});

// 在Service Worker中监听消息并触发自定义事件
chrome.runtime.onMessage.addListener((message) => {
  if (message.event === 'listTypeSet') {
    self.dispatchEvent(new Event('listTypeSet'));
  }
});

原方案阻塞原因

之前的轮询依赖setTimeout,但Service Worker可能因空闲进入休眠状态,导致定时器无法按时触发;此外轮询逻辑未绑定变量变化的事件,可能出现currListType已设置但轮询函数未及时检测到的情况。消息驱动或自定义事件的方式更贴合Service Worker的事件模型,避免了休眠带来的问题。

内容的提问来源于stack exchange,提问作者VladTbk321

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 20:17:52