IndexedDB游标遍历存在异步并发竞争风险,因其一次性、实时反映数据状态且绑定事务生命周期;应避免边读边写、跨事务依赖,并采用单事务串行读取+批量处理等策略保障一致性。

HTML5 中的游标(Cursor)本身不直接涉及异步并发竞争——真正需要关注的是 IndexedDB 的游标遍历过程与异步操作之间的时序冲突。IndexedDB 是 HTML5 提供的客户端数据库,其游标(IDBCursor)用于顺序读取对象存储(Object Store)中的记录,而所有操作均为异步、基于事件或 Promise(通过 IDBRequest 或封装库如 idb)。所谓“并发竞争”,通常指:多个游标操作或游标 + 修改操作(如 add/delete/put)同时进行,导致数据状态不一致、游标越界、重复处理或跳过记录等问题。
游标生命周期与异步执行的本质冲突
IDBCursor 是一次性的、只进的、绑定在特定事务生命周期内的迭代器。它不保存快照,而是实时反映底层数据状态:
- 游标移动(
continue()或advance())必须在当前事务未完成前调用,否则抛出InvalidStateError; - 若在游标遍历中,同一事务或另一事务修改了正在遍历的对象存储(如删除当前键对应记录),后续
continue()可能返回null或跳转到下一个有效键,行为不可预测; - 多个游标并行打开(尤其跨不同事务)不会相互阻塞,但彼此看到的数据视图取决于各自事务的隔离级别(IndexedDB 实际采用“读已提交”近似语义,无真正快照隔离)。
避免竞态的实践原则
核心思路是:**不共享游标、不混用读写、不跨事务依赖中间状态**。
- 单事务内串行遍历 + 批量处理:在一个只读事务中完成全部游标读取,收集所需数据(如主键数组),再在新事务中执行写操作。避免边读边写;
-
禁用游标期间的并发写入:若业务必须边遍历边更新,确保所有读写都在同一事务中,并使用
readwrite模式——但注意:游标遍历中调用put()或delete()不影响当前游标位置,但可能影响后续continue()的结果(例如删除当前项后,下一次continue()会跳到再下一个); -
用 keyRange 精确约束范围,避免动态变化干扰:初始化游标时传入稳定
IDBKeyRange(如IDBKeyRange.bound('a', 'z')),比无范围遍历更可控; -
避免在游标回调中启动新异步任务并持有游标引用:例如在
onsuccess中发起 fetch 请求,然后试图在响应回来后再continue()——此时原事务很可能已结束,游标失效。
推荐写法:Promise 封装 + 递归遍历(安全模式)
以下是一个健壮的游标遍历封装示例(使用现代 Promise 风格,基于 idb 库或手动 promisify):
async function iterateAll(db, storeName, handler) {
const tx = db.transaction(storeName, 'readonly');
const store = tx.objectStore(storeName);
const cursorReq = store.openCursor();
return new Promise((resolve, reject) => {
cursorReq.onsuccess = async function (e) {
const cursor = e.target.result;
if (!cursor) return resolve();
try {
await handler(cursor.value, cursor.key, cursor);
cursor.continue(); // 继续下一项
} catch (err) {
reject(err);
}
};
cursorReq.onerror = () => reject(cursorReq.error);
});
}
// 使用示例:仅读取,不修改
iterateAll(db, 'users', (value, key) => {
console.log(`处理用户 ${key}:`, value.name);
});
关键点:handler 是 async,但 cursor.continue() 严格在上一项处理完成(包括 await 结束)后才调用,形成逻辑串行;整个过程锁死在单事务中,无外部并发干扰。
特殊情况:需边遍历边更新?用“两阶段提交”模拟
例如批量更新满足条件的记录:
- 第一阶段:只读事务中遍历,收集待更新的 key 数组;
- 第二阶段:新建 readwrite 事务,循环调用
store.put(newVal, key); - 优势:读写分离,规避游标与修改的耦合;失败可重试某条,不影响整体进度。
若必须原子性保障(如金融类场景),IndexedDB 本身不支持回滚到中间状态,应改用服务端协调,或前端引入本地锁机制(如用 localStorage 标记处理状态,配合超时清理)——但这已超出 HTML5 原生能力范畴。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










