IndexedDB 事务自动管理生命周期,持续到所有挂起请求及回调执行完毕;在 onsuccess 中发起新 IDB 请求可延续事务,但 fetch、setTimeout、Promise.then 等异步操作不延长事务;任一请求失败默认中止整个事务,需 event.preventDefault() 阻止;并发 write 事务串行执行,应合并批量操作并读写分离。

IndexedDB 的事务不是手动开启再手动提交的,而是自动管理生命周期的。它一旦创建,就会持续到所有挂起请求完成、微任务队列清空为止;只要还有 pending 请求或回调未执行完,事务就保持活跃;一旦空闲,立刻尝试提交——这个“自动提交”特性是多数问题的根源。
事务生命周期由请求队列驱动
IDB 事务的存活不取决于你写了多少行代码,而取决于当前是否还有未完成的请求(如 get、put、add)及其关联的 onsuccess/onerror 回调是否已执行完毕。浏览器会等待这些回调执行完,并确保没有新的请求被发起,才关闭事务。
- 在 request.onsuccess 中发起新请求(比如 put 后紧接着 add),事务会延续
- 若在 onsuccess 里调用 setTimeout 或 fetch,这些异步操作不会延长事务寿命,事务大概率已在回调返回后就关闭了
- Promise.then 里的操作同理——它属于微任务,但事务不等微任务,只等 IDB 请求本身
错误传播会直接中止整个事务
事务内任意一个请求失败(如 ConstraintError、AbortError),默认会触发 transaction.onabort 并中止全部后续操作。这不是“某个请求失败”,而是“整笔事务作废”。
- 想让失败不中断事务,必须在对应 request.onerror 中调用 event.preventDefault()
- 例如插入重复 key 报 ConstraintError,preventDefault 后可继续执行后续 put 或 add
- 但注意:preventDefault 只阻止中止,不修复错误,业务逻辑需自行处理(如改 key、跳过、重试)
并发写入需要显式协调
多个 readwrite 事务同时操作同一个 objectStore 时,后启动的事务会排队等待前一个完成。这不是 JS 层面的锁,而是 IndexedDB 引擎级的阻塞机制。
- Promise.all 并发发起多个事务,实际是串行执行,且容易因前一个事务耗时长导致后几个超时或失败
- 高频操作建议合并为单个事务(批量 put/add),而非拆成多个小事务
- 读写分离:只读查询用 readonly 事务,避免与 readwrite 争抢对象存储锁
回调执行顺序影响事务稳定性
IDB 请求的回调(onsuccess/onerror)是同步进入事件循环的,但它们的执行时机受 JS 执行栈和微任务队列影响。关键点在于:事务不等待 Promise 链,只认原生 IDB 请求。
- 错误写法:request.onsuccess = () => fetch('/log').then(() => store.add(log)) —— fetch 不属于事务,add 可能因事务已关闭而报 InvalidStateError
- 正确做法:把所有 IDB 操作(get/put/add)都放在 onsuccess 内同步发起;异步副作用(如日志上报、UI 更新)放到事务外
- 调试技巧:在 transaction.oncomplete 和 transaction.onabort 中加 console.log,观察事务是否如预期结束

















