You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

链式Promise转换的Observable与from+concatMap生成的Observable是否等价?

问题解答

错误根因

  • 该约束错误和Dexie/IndexedDB的clear实现无关,clear返回Promise resolve时已经完成了表格清空,不存在提前返回的问题。
  • 错误本质是快速连续两次调用setItemInfos时,两个独立的异步操作链出现了执行顺序竞态,和单次调用时的链式逻辑无关。

第一种实现的问题

单次调用第一种写法的逻辑是完全符合预期的,clear执行完成后才会触发bulkAdd。但连续触发两次调用时会出现如下异常时序:

  1. 第一次调用触发异步清空任务clear_1,返回待执行的Observable
  2. 第二次调用触发异步清空任务clear_2,返回待执行的Observable
  3. clear_1执行完成,触发批量插入任务bulkAdd_1
  4. clear_2执行完成,触发批量插入任务bulkAdd_2
  5. 若bulkAdd_2比bulkAdd_1先执行完成,bulkAdd_1插入时就会遇到已存在的主键,抛出约束错误。

两种实现的等价性判断

两种实现完全不等价,你当前第二种写法未复现错误仅为巧合:
from(Promise)接收Promise参数时,Promise会立即执行,也就是说你声明bulkAdd$的那一刻,bulkAdd操作就已经触发了,并不会等待clear$执行完成。现在未复现错误只是测试场景下clear执行速度恰好快于bulkAdd,如果遇到大表清空耗时更长的场景,不仅会出现主键冲突,甚至可能出现bulkAdd先完成、clear后执行,最终所有数据被清空的异常。

如果要使用RxJS运算符实现正确的串行逻辑,应该将bulkAdd的Observable创建放在concatMap的回调中,确保clear完成后才触发插入操作:

public setItemInfos(itemInfos: IItemInfo[]): Observable<number> {
  return from(this.db.selectionItemInfos.clear()).pipe(
    concatMap(() => from(this.db.selectionItemInfos.bulkAdd(itemInfos)))
  );
}

彻底解决竞态的方案

如果要完全避免快速双击导致的并发问题,建议在上层调用setItemInfos的逻辑中增加防抖,或使用switchMap/concatMap管控调用顺序,避免同时存在多个未完成的异步读写流。


内容的提问来源于stack exchange,提问作者char m

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 15:45:00