为什么我的IndexedDB事务在PWA中偶尔会突然中止?
我在开发PWA离线功能时用IndexedDB存数据,但发现当用户快速切换页面时,有时候会突然报错”Transaction inactive”,数据库操作直接中断。我按照MDN文档把事务写在async函数里,像这样:const tx = db.transaction(['todos'], 'readwrite'),但还是会出现问题。尝试过在beforeunload事件里commit事务,但控制台还是显示AbortError。有次明明数据写入成功了,但onerror回调却触发了…
const saveData = async () => {
const tx = db.transaction(['notes'], 'readwrite');
const store = tx.objectStore('notes');
const request = store.add({ title: 'test', content: 'hello' });
try {
await request.complete; // 这里偶尔会直接跳到catch
} catch(err) {
console.error('存储失败:', err); // 出现AbortError时如何处理?
}
};
最直接的解决办法是不要提前 await request.complete,而是等事务真正完成。你可以用 tx.commit() 主动提交,但更稳妥的做法是监听 tx.oncomplete:
const saveData = async () => {
const tx = db.transaction(['notes'], 'readwrite');
const store = tx.objectStore('notes');
const request = store.add({ title: 'test', content: 'hello' });
// 等待事务真正完成
await new Promise((resolve, reject) => {
tx.oncomplete = resolve;
tx.onerror = () => reject(tx.error);
tx.onabort = () => reject(tx.error);
});
};
这样写之后,事务就不会在中途被提前关闭了。另外,beforeunload 里面 commit 事务是没错的,但你要确保那个时候没有其他异步操作还在跑,否则也会被浏览器中断。你可以加个标记,比如 window.isDataSaving = true,等事务真正完成后再设为 false,beforeunload 里判断这个标记决定是否等待。
还有一点容易被忽略:如果你在同一个页面开多个事务,记得它们之间不要互相干扰。IndexedDB 的事务是基于事件循环的,一个事件循环里开的事务会“锁住”数据库一段时间,等事件循环结束才会释放。所以如果你在多个函数里都开了事务,最好复用一个。
最后一条建议:插件可以省点事,比如 idb 或 localforage 这类封装库,内部已经帮你处理了这些生命周期问题。如果不想引入依赖,那就按照上面的方式改,应该就能稳住了。