为何使用map、forEach和for循环向数据库插入数组时顺序存在差异
问题原因解析
核心差异在于异步执行逻辑的不同
map和forEach属于数组的同步遍历方法,本身不支持等待异步回调执行完成:- 你传入的
async回调函数遇到await this.repository.save(item)时,会立刻返回一个 pending 状态的 Promise,map/forEach拿到这个返回值后不会做任何等待处理,马上就会遍历下一个元素、触发下一个save请求。 - 所有save请求是并发发往数据库的,最终哪个请求先完成、先写入数据库完全取决于网络耗时和数据库的处理调度,所以存储顺序和原数组顺序无关,会出现随机乱序的情况。
- 额外补充:就算你对
map返回的Promise数组调用await Promise.all(...),也只是等待所有请求全部完成,所有请求依然是并发执行,写入顺序还是无法保证。
- 你传入的
- 普通for循环(以及
for...of循环)支持异步串行执行:- 循环内部的
await会暂停当前整个循环的执行,直到当前的this.repository.save(item)请求完成、数据库写入成功后,才会进入下一次循环处理下一个元素。 - 所有save请求是严格按数组顺序依次执行的,所以最终存储顺序和原数组完全一致。
- 循环内部的
替代写法参考
如果你不想使用基础for循环,也可以选择以下写法保证写入顺序:
for...of串行写法,可读性更高:
for (const item of arrayChild) { await this.repository.save(item) }
- 如果希望通过并发请求提升性能,同时保证最终存储顺序,可以先并发拿到所有处理结果,再按原数组顺序批量写入:
// 并发处理所有元素,拿到处理后的结果数组,顺序和原数组一致 const processedItems = await Promise.all(arrayChild.map(item => this.repository.save(item))) // 调用批量插入方法一次性写入,顺序和原数组保持一致(需ORM支持批量插入接口) await this.repository.insert(processedItems)
内容的提问来源于stack exchange,提问作者Đoàn Đức Bảo
相关产品推荐
相关产品推荐

