Sequelize更新记录时MySQL与SQLite的.forEach()行为不一致问题
Sequelize中forEach异步更新MySQL不生效,for-of正常的问题分析与解决
问题现象
使用Node.js 20.12.2 + Sequelize 6.37.3搭配MySQL时,通过.forEach()遍历findAll()结果更新记录,更新操作未生效;但使用for (const ... of ...)循环时更新正常。而在SQLite环境下,两种遍历方式都能正确更新。
测试代码如下:
const Sequelize = require('sequelize'); let db; if (0) { // SQLite case: db = new Sequelize({ dialect: 'sqlite', storage: ':memory:', }); } else { // MySQL case: db = new Sequelize({ dialect: 'mysql', hostname: '127.0.0.1', username: 'root', password: process.env.DB_PASSWORD, database: 'test', }); } (async () => { const Foo = db.define('Foo', { bar: Sequelize.DataTypes.STRING, }); // 创建表 await Foo.sync({ force: true }); // 插入测试数据 await Foo.create({ bar: 'abc', }); // forEach 测试 let items = await Foo.findAll(); console.log(items[0].bar); items.forEach(async item => { item.bar = 'forEach'; await item.save(); }); items = await Foo.findAll(); console.log(items[0].bar); // for-of 测试 items = await Foo.findAll(); console.log(items[0].bar); for (const item of items) { item.bar = 'for-of'; await item.save(); }; items = await Foo.findAll(); console.log(items[0].bar); })().finally(() => { process.exit(); });
执行日志对比
- SQLite日志:forEach更新后,后续
findAll能读取到forEach值 - MySQL日志:forEach更新后,后续
findAll仍读取到旧值abc,直到for-of执行后才更新为for-of
问题原因
核心差异在于异步函数的等待机制:
.forEach()方法不会等待传入的异步函数执行完成,调用后会立即继续执行后续代码。MySQL是磁盘数据库,IO操作有延迟,导致await Foo.findAll()在item.save()完成前就执行,读取到的是未更新的数据。for-of循环会在每次迭代中等待await item.save()完成,确保更新操作执行完毕后才进入下一步,后续的findAll能读取到最新数据。- SQLite作为内存数据库,IO速度极快,forEach中的
save()可能刚好在findAll()前完成,导致看起来生效,但这是不可靠的偶然情况。
解决方案
将forEach替换为支持异步等待的遍历方式,两种可选方案:
方案1:使用for-of循环(已验证有效)
保持现有for-of的写法,确保每次更新都等待完成:
for (const item of items) { item.bar = 'forEach'; await item.save(); }
方案2:使用Promise.all()配合map()
适合需要并行执行更新的场景,等待所有更新完成后再执行后续操作:
await Promise.all(items.map(async item => { item.bar = 'forEach'; await item.save(); }));
修改后,MySQL环境下也能正确读取到更新后的数据。
内容的提问来源于stack exchange,提问作者Gea-Suan Lin
相关产品推荐
相关产品推荐

