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

Chrome扩展切换标签后IndexedDB查询返回Promise undefined问题

问题原因解答

首先回答你的疑问:Pre-promise reached!未打印,说明get_records函数确实被触发了,但函数内的if(db)判断未通过,函数直接结束没有返回值,因此你得到了undefined,报错出现在调用undefined的then方法时。

核心问题点

  1. Service Worker 休眠导致变量丢失:Chrome Manifest V3的background是Service Worker,长时间无活动会被自动销毁,所有全局变量(包括你定义的db)都会被清空。当你切回页面打开popup时,Service Worker被唤醒重新执行代码,IndexedDB的打开是异步操作,你调用get_records时db还未完成初始化赋值,所以if(db)判断为假。
  2. popup事件绑定顺序错误:你现有popup代码中,自动触发按钮点击的逻辑在事件绑定之前执行,首次自动触发时事件监听还未注册,不会发送获取笔记的请求,手动点击时事件已绑定完成,所以能正常运行。
  3. IndexedDB 配置错误:创建对象仓库时你写的keypath是小写,正确属性名是大小写敏感的keyPath,这个问题会导致后续插入数据时出错。
  4. 消息监听未保持端口开放: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 10:18:03