Chrome扩展切换标签后IndexedDB查询返回Promise undefined问题
问题原因解答
首先回答你的疑问:Pre-promise reached!未打印,说明get_records函数确实被触发了,但函数内的if(db)判断未通过,函数直接结束没有返回值,因此你得到了undefined,报错出现在调用undefined的then方法时。
核心问题点
- Service Worker 休眠导致变量丢失:Chrome Manifest V3的background是Service Worker,长时间无活动会被自动销毁,所有全局变量(包括你定义的
db)都会被清空。当你切回页面打开popup时,Service Worker被唤醒重新执行代码,IndexedDB的打开是异步操作,你调用get_records时db还未完成初始化赋值,所以if(db)判断为假。 - popup事件绑定顺序错误:你现有popup代码中,自动触发按钮点击的逻辑在事件绑定之前执行,首次自动触发时事件监听还未注册,不会发送获取笔记的请求,手动点击时事件已绑定完成,所以能正常运行。
- IndexedDB 配置错误:创建对象仓库时你写的
keypath是小写,正确属性名是大小写敏感的keyPath,这个问题会导致后续插入数据时出错。 - 消息监听未保持端口开放:background的消息监听内执行异步操作时没有返回
true,Chrome会提前关闭消息端口,导致异步结果无法回传。
修复方案
1. 重写background.js的DB操作逻辑,封装异步初始化
// background.js let dbPromise; // 封装DB初始化逻辑,返回Promise确保每次调用时DB已就绪 function getDB() { if (!dbPromise) { dbPromise = new Promise((resolve, reject) => { const request = self.indexedDB.open('MyTestDB'); request.onerror = (event) => { console.log("打开DB失败", event.target.error); reject(event.target.error); } request.onupgradeneeded = (event) => { const db = event.target.result; // 修正keypath为keyPath的大小写错误 const objectStore = db.createObjectStore('notes', { keyPath: "id", autoIncrement: true }); objectStore.createIndex("videoID, videoTime", ["videoID", "videoTime"], {unique: false}); objectStore.transaction.oncomplete = () => { console.log("对象仓库创建完成"); } } request.onsuccess = (event) => { const db = event.target.result; console.log("DB打开成功"); // DB关闭时重置promise,下次调用重新初始化 db.onclose = () => { dbPromise = null; } resolve(db); } }) } return dbPromise; } // 改造get_records为异步函数,先等待DB初始化完成 async function get_records(vidID) { const db = await getDB(); const get_transaction = db.transaction("notes", "readonly"); const objectStore = get_transaction.objectStore("notes"); const myIndex = objectStore.index("videoID, videoTime"); console.log("Pre-promise reached!"); return new Promise((resolve, reject) => { get_transaction.oncomplete = () => { console.log("所有查询事务完成"); } get_transaction.onerror = (event) => { console.log("查询笔记出错", event.target.error); reject(event.target.error); } const request = myIndex.getAll(IDBKeyRange.bound([vidID], [vidID, []])); request.onsuccess = (event) => { console.log(event.target.result); resolve(event.target.result); } }); } // 改造消息监听,异步操作时return true保持端口开放 chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if(request.message === 'get_notes') { get_records(request.payload) .then(res => { chrome.runtime.sendMessage({ message: 'getNotes_success', payload: res }) }) .catch(err => { console.error("获取笔记失败", err); chrome.runtime.sendMessage({ message: 'getNotes_success', payload: [] }) }) // 必须返回true,告知Chrome保持消息端口开放等待异步结果 return true; } });
2. 调整popup.js的执行顺序,修正事件绑定问题
// popup.js // 先绑定按钮点击事件,再执行自动触发逻辑 refNotesbtn.addEventListener('click', function() { videoID = get_videoID(); chrome.runtime.sendMessage({ message: 'get_notes', payload: videoID }); }); chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.message === 'getNotes_success') { notesTable.innerHTML = ""; // 改为判断数组长度更稳妥,避免空数组误判 if (request.payload?.length) { // 展示笔记的业务代码 } else { // 展示提示:请添加笔记或刷新表格 } } }); chrome.tabs.query({active: true, currentWindow: true}, (tabs) => { site = tabs[0].url; if (isYTVid.test(site)) { // 此时事件已绑定完成,自动触发能正常发送请求 refNotesbtn.click(); } });
内容的提问来源于stack exchange,提问作者A Ahad
相关产品推荐
相关产品推荐

