IndexedDB初始化延迟致db未定义,事务创建失败求解决方案
解决IndexedDB初始化异步导致的
db未定义问题 你碰到的问题本质是IndexedDB的数据库连接是异步建立的——indexedDB.open()不会立刻返回可用的数据库实例,而是要等到onsuccess或onupgradeneeded回调触发后,db变量才会被赋值。但你的代码在初始化完成前就直接调用了Dairy.add()和Dairy.all(),这时候db还是undefined,自然会抛出Cannot read property 'transaction' of undefined的错误。
修复方案:用Promise封装数据库初始化
我们可以把数据库的初始化过程包装成一个Promise,让所有数据库操作都等待这个Promise完成后再执行,确保db已经就绪。以下是修改后的完整代码:
// DATABASE FOR THE DIARY const Dairy = (() => { if (window.indexedDB) { let db; const DB_NAME = 'Dairy'; const DB_VERSION = 1; const DB_STORE_NAME = 'diaries'; // 新增:创建一个初始化Promise,等待数据库连接完成 const dbInitPromise = new Promise((resolve, reject) => { const request = window.indexedDB.open(DB_NAME, DB_VERSION); request.onerror = (event) => { console.log('Error requesting to open database permission denied.'); reject(event.target.error); }; request.onsuccess = (event) => { console.log('there is indexeddb!'); db = event.target.result; console.log('db: ', db); db.onerror = (evt) => { console.error(`Database error: ${evt.target.errorCode}`); }; resolve(db); // 初始化完成,resolve Promise }; request.onupgradeneeded = (event) => { db = event.target.result; const store = db.createObjectStore(DB_STORE_NAME, { keyPath: 'date' }); store.createIndex('subject', 'subject', { unique: false }); store.createIndex('date', 'date', { unique: true }); store.createIndex('description', 'description', { unique: false }); // 升级完成后,db已经可用,resolve Promise resolve(db); }; }); const getObjectStore = async (storeName, mode) => { // 等待数据库初始化完成 await dbInitPromise; console.log('db from getObjectStore: ', db); const tx = db.transaction(storeName, mode); return tx.objectStore(storeName); }; const getAllEntries = async () => { const store = await getObjectStore(DB_STORE_NAME, 'readonly'); return new Promise((resolve) => { const result = []; const req = store.openCursor(); req.onsuccess = (evt) => { const cursor = evt.target.result; if (cursor) { // 注意:数组用push而不是append result.push(cursor.value); cursor.continue(); } else { console.log('No more entries'); resolve(result); } }; }); }; const setAnEntry = async (value) => { const store = await getObjectStore(DB_STORE_NAME, 'readwrite'); // 用Promise包装put操作,方便调用者处理结果 return new Promise((resolve, reject) => { const request = store.put(value); request.onsuccess = () => resolve(request.result); request.onerror = () => reject(request.error); }); }; const removeAnEntry = async (value) => { const store = await getObjectStore(DB_STORE_NAME, 'readwrite'); return new Promise((resolve, reject) => { const request = store.remove(value); request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); }; return { all: getAllEntries, add: setAnEntry, remove: removeAnEntry, }; } alert("Your browser doesn't support a stable version of IndexedDB. Dairy related features will not be available."); return {}; })(); // 注意:现在所有方法都是异步的,需要用async/await处理 (async () => { console.log('adding something in the diary'); try { await Dairy.add({ subject: 'hello', description: 'bluh bluh', date: new Date().toISOString() }); console.log('Entry added successfully'); const allEntries = await Dairy.all(); console.log('show all: ', allEntries); } catch (err) { console.error('Operation failed: ', err); } })();
关键修改点说明
- 数据库初始化Promise:用
dbInitPromise封装indexedDB.open()的异步过程,在onsuccess和onupgradeneeded回调中resolve这个Promise,确保后续操作能等待db就绪。 - 异步化所有操作:将
getObjectStore、getAllEntries、setAnEntry、removeAnEntry都改成异步函数(用async/await),确保每次操作前都等待数据库初始化完成。 - 修复数组操作错误:原来的
result.append(value)改为result.push(value),因为JavaScript数组没有append方法。 - 用Promise包装数据库操作:给
put和remove操作添加Promise封装,让调用者可以方便地处理成功/失败的情况。 - 异步调用Dairy方法:因为现在所有方法都是异步的,所以需要用
async/await或者.then()来调用,避免直接同步调用导致的问题。
这样修改后,就能保证所有数据库操作都在db初始化完成后执行,不会再出现db未定义的错误了。
内容的提问来源于stack exchange,提问作者ASB
相关产品推荐
相关产品推荐

