添加页面加载监听await后Chrome扩展程序停止执行问题
Chrome扩展替换sleep为页面加载监听后陷入无限等待
我写的Chrome扩展会执行一系列操作,原本每步操作之间用sleep(1000)来等待页面完全加载。现在想把sleep替换成监听页面加载完成的逻辑,但修改后程序陷入无限等待,操作序列没法继续执行。
原代码
chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'triggerAction') { console.log('Triggering action'); chrome.tabs.create({ url: urlToVisit }, (tab) => { console.log('TAB ID: ', tab.id); (async function () { for (let action of actions) { console.log('ABOUT TO EXECUTE SCRIPT', action.type); chrome.scripting.executeScript( { target: { tabId: tab.id }, func: performAction, args: [action] }, () => { console.log('Content script injected'); }); await sleep(1000); // 1 second pause } console.log('SCRIPT EXECUTED'); })(); }) } });
修改后的问题代码
chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'triggerAction') { console.log('Triggering action'); chrome.tabs.create({ url: urlToVisit }, (tab) => { console.log('TAB ID: ', tab.id); (async function () { for (let action of actions) { await new Promise(resolve => { chrome.scripting.executeScript( { target: { tabId: tab.id }, func: () => { window.addEventListener('DOMContentLoaded', resolve); } }); }); console.log('ABOUT TO EXECUTE SCRIPT', action.type); chrome.scripting.executeScript( { target: { tabId: tab.id }, func: performAction, args: [action] }, () => { console.log('Content script injected'); }); } console.log('SCRIPT EXECUTED'); })(); }) } });
问题原因
你当前的写法有两个致命问题导致无限等待:
DOMContentLoaded事件仅触发一次:如果你的操作是在同一个已加载完成的页面内执行,后续注入脚本时这个事件早就触发过了,监听永远不会生效,Promise一直处于pending状态。- 上下文不匹配:注入的内容脚本里的
resolve是在页面的上下文,而后台脚本里的Promise属于扩展后台上下文,两者完全隔离,根本无法触发Promise的完成逻辑。
解决方案
根据你的操作场景不同,有两种可行的解决方式:
场景1:每次操作针对新打开的页面
如果你的actions是依次在新页面执行,直接监听tabs.onUpdated事件,等待页面状态变为complete即可:
chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'triggerAction') { console.log('Triggering action'); chrome.tabs.create({ url: urlToVisit }, (tab) => { console.log('TAB ID: ', tab.id); (async function () { for (let action of actions) { // 等待页面加载完成 await new Promise(resolve => { const listener = (tabId, changeInfo) => { if (tabId === tab.id && changeInfo.status === 'complete') { chrome.tabs.onUpdated.removeListener(listener); resolve(); } }; chrome.tabs.onUpdated.addListener(listener); }); console.log('ABOUT TO EXECUTE SCRIPT', action.type); await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: performAction, args: [action] }); console.log('Content script injected'); } console.log('SCRIPT EXECUTED'); })(); }) } });
场景2:操作在同一个页面内执行
如果是在同一个页面内连续执行多步操作,不需要监听DOMContentLoaded,而是让内容脚本主动通知后台操作完成,或者在内容脚本内等待目标元素加载:
方案A:内容脚本执行完成后通知后台
修改performAction,操作完成后给后台发消息:
// 后台脚本 chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'triggerAction') { console.log('Triggering action'); chrome.tabs.create({ url: urlToVisit }, (tab) => { console.log('TAB ID: ', tab.id); (async function () { // 先等初始页面加载完成 await new Promise(resolve => { const listener = (tabId, changeInfo) => { if (tabId === tab.id && changeInfo.status === 'complete') { chrome.tabs.onUpdated.removeListener(listener); resolve(); } }; chrome.tabs.onUpdated.addListener(listener); }); for (let action of actions) { await new Promise(resolve => { // 监听内容脚本的完成通知 const msgListener = (msg, sender) => { if (sender.tab.id === tab.id && msg.action === 'actionDone') { chrome.runtime.onMessage.removeListener(msgListener); resolve(); } }; chrome.runtime.onMessage.addListener(msgListener); // 注入执行脚本 console.log('ABOUT TO EXECUTE SCRIPT', action.type); chrome.scripting.executeScript({ target: { tabId: tab.id }, func: performAction, args: [action] }); }); } console.log('SCRIPT EXECUTED'); })(); }) } }); // performAction函数 function performAction(action) { // 执行你的操作逻辑,比如点击、输入等 console.log('Executing action:', action.type); // 操作完成后发送消息给后台 chrome.runtime.sendMessage({ action: 'actionDone' }); }
方案B:内容脚本内等待目标元素加载
如果操作需要等待某个元素出现,直接在performAction里做等待逻辑:
// 后台脚本 chrome.runtime.onMessage.addListener((message, _sender, _sendResponse) => { if (message.action === 'triggerAction') { console.log('Triggering action'); chrome.tabs.create({ url: urlToVisit }, (tab) => { console.log('TAB ID: ', tab.id); (async function () { // 等待初始页面加载完成 await new Promise(resolve => { const listener = (tabId, changeInfo) => { if (tabId === tab.id && changeInfo.status === 'complete') { chrome.tabs.onUpdated.removeListener(listener); resolve(); } }; chrome.tabs.onUpdated.addListener(listener); }); for (let action of actions) { console.log('ABOUT TO EXECUTE SCRIPT', action.type); // 等待脚本执行完成(内部已处理元素等待) await chrome.scripting.executeScript({ target: { tabId: tab.id }, func: performAction, args: [action] }); console.log('Action completed'); } console.log('SCRIPT EXECUTED'); })(); }) } }); // performAction函数,内置元素等待逻辑 function performAction(action) { return new Promise(resolve => { const checkTarget = () => { // 假设action里包含目标元素的选择器 const target = document.querySelector(action.selector); if (target) { // 执行操作,比如点击 target.click(); resolve(); } else { // 每隔100ms检查一次,直到元素出现 setTimeout(checkTarget, 100); } }; checkTarget(); }); }
内容的提问来源于stack exchange,提问作者Anatole Martin
相关产品推荐
相关产品推荐

