indexeddb游标是控制数据流动节奏的阀门,需显式调用continue()或advance(n)逐条推进,配合idbkeyrange实现分页与条件查询,并通过时间切片和异常处理保障性能与健壮性。

IndexedDB 游标不是“取数据的快捷方式”,而是控制数据流动节奏的阀门。它不加载全部内容,只在需要时拉取下一条,天然适合处理几千到百万级本地数据——关键不在“怎么动”,而在“怎么控”。
游标遍历的核心逻辑:逐条推进 + 显式控制
游标不会自动走完所有记录,必须靠 continue() 或 advance(n) 主动驱动。每次调用后,浏览器异步执行一次微任务,拿到下一条数据再回调。这避免了同步循环卡死主线程,也防止内存堆积。
- 用 openCursor() 启动,传入 IDBKeyRange 可限制起点和终点(比如只读 timestamp 在某区间内的日志)
- cursor.key 是当前记录主键,cursor.value 是完整对象,两者配合可做条件过滤或分页锚点
- 不要在 onsuccess 里写 while 循环,那是常见误区;应递归调用 continue(),或用 async/await 封装成线性流程
分页读取:用 lastKey 实现稳定“下一页”
跳过前 N 条(如 offset=200)看似直观,但 IndexedDB 不支持 SQL 式偏移,且 cursor.advance() 在复合主键或非连续 key 下不可靠。更健壮的做法是记录上一页最后一条的 key,作为下一页查询的下界。
- 假设上一页最后一条主键是 123,下一页请求用 IDBKeyRange.lowerBound(123, true)(true 表示排除 123 本身)
- 这个方法要求主键可排序(number、ISO 时间字符串、自增 ID 都行),且索引已按该字段排序
- 若查的是索引字段(如 email),需调用 index.openCursor(range),range 要基于索引值,不是主键
性能优化:分批推进 + 时间切片
面对 10 万条日志,逐条 continue() 会排队数万个微任务,UI 响应迟滞。实际中应分组处理,主动让出主线程。
- 每处理 50–200 条后,调用 cursor.advance(100) 跳过一批,比连续 100 次 continue() 更轻量
- 用 queueMicrotask 或 setTimeout(..., 0) 包裹下一批推进,实现“时间切片”,保持页面可交互
- 边扫边清:拿到 cursor.value 后立即做业务处理(如写入新 store、生成 Blob、匹配关键词),处理完即丢弃引用,不累积数组
异常与中断:必须考虑的现实问题
游标遍历常用于导出、同步、聚合等长耗时操作,用户可能中途取消,或事务超时、存储空间不足。这些不能靠 try/catch 捕获。
- 给事务设置超时:transaction.duration = 60000(部分浏览器支持),或手动监控耗时
- 加退出开关:if (abortSignal?.aborted) return,配合按钮点击或 AbortController 控制流程
- 错误必须监听 request.onerror 和 transaction.onabort,不能只靠 success 回调











