indexeddb大批量操作假死源于事务过大阻塞主线程,需分片(每批50–100条)、控制节奏、结果仅在onsuccess/onerror中有效,并配合ui反馈与分页查询优化。

页面假死在 IndexedDB 大批量操作中很常见,根本原因不是“异步”失效,而是事务过大、阻塞主线程太久。关键不在于是否异步,而在于事务粒度和执行节奏是否可控。
事务必须分片,不能堆在一个里
单个事务插入上千条记录,浏览器必须等它全部完成才能响应 UI。哪怕 API 是异步的,底层仍会锁住主线程——这不是 bug,是 IndexedDB 的设计机制。
- 推荐每批 50–100 条,具体看单条数据大小(含二进制 blob 时需更小)
- 用 for 循环 + await Promise 控制批次顺序,避免并发太多压垮内存
- 每次事务结束后触发
tx.oncomplete,才算真正释放主线程
别直接调 result,结果必须等 success 回调
很多假死其实源于误判异步状态:比如在 request 发出后立刻读 request.result,这会返回 undefined 或抛错,还可能引发未捕获异常拖慢渲染。
- 所有读写操作的结果,只在
onsuccess或onerror里才有效 - 封装成 Promise 时,resolve 必须放在 onsuccess 内,reject 对应 onerror
- 避免在事件外访问 IDBRequest 的 result、error 等属性
批量写入时加节流提示,用户感知更稳
即使拆分了事务,1000 条也要几十次往返。用户点击后长时间没反馈,容易误以为卡死。
- 在循环开始前显示“正在导入…”,每完成一批更新进度(如 “已存 200/1000”)
- 可配合
setTimeout(() => {}, 0)强制让 UI 刷新一次,避免文字不更新 - 若中途失败,保留已成功批次,提供“继续从第 X 条开始”选项
读操作也别一次拉全量
游标遍历或 getAll() 加载几万条记录,同样会让 JS 主线程忙几百毫秒,造成卡顿。
- 用 cursor.open() 配合 limit + offset 实现真分页,每次只取一页(如 50 条)
- 查询带索引字段时,优先用 index.get() 或 index.openCursor(),避免全表扫描
- 对列表类场景,先查总数、再按需加载,而非默认拉空数组再 push











