使用forEach迭代对象写入JSON文件时仅最后一个对象生效的问题及解决方法
问题分析与解决方案
这个问题的核心是异步操作的并发冲突,我来帮你拆解清楚:
你用array.forEach()遍历数组时,会立刻触发所有writeTheData函数的调用,但fs.readFile是异步方法——这意味着所有的读取操作几乎同时启动,它们读到的都是fb.json的原始内容(还没有任何修改的版本)。
每个函数实例都会基于这份原始数据修改出自己的newsData,然后依次执行写入。后面的写入操作会直接覆盖前面的,因为前面的修改还没来得及保存到文件里,后面的就已经基于原始数据完成修改并写入了。这就是为什么最后只有最后一个对象被保留下来——它是最后一个完成写入的,覆盖了之前所有的修改。
虽然你用obj.link作为唯一键,但这解决的是对象本身的覆盖问题,解决不了异步并发读写带来的文件内容覆盖。
修复方案(两种推荐方式)
方案1:串行处理异步操作(逐个执行)
把writeTheData改成异步函数,用async/await确保每次读写完成后再处理下一个对象,彻底避免并发冲突。
首先切换到Promise版的fs模块:
const fs = require('fs').promises;
然后修改你的函数:
async function writeTheData(obj) { try { // 异步读取文件,等待读取完成 const data = await fs.readFile('./assets/data/fb.json'); let newsData = JSON.parse(data); // 更新数据 newsData[obj.link] = { ...newsData[obj.link], link: obj.link, title: obj.title, text: obj.contentSnippet, articleDate: obj.pubDate, content: obj.content }; // 异步写入文件,等待写入完成 await fs.writeFile("./assets/data/fb.json", JSON.stringify(newsData)); console.log(`Successfully wrote data for: ${obj.title}`); } catch (err) { console.error('Error processing data:', err); } }
调用时不要用forEach(它不支持异步等待),改用for...of循环:
(async () => { for (const obj of arr) { await writeTheData(obj); } })();
方案2:批量处理(更高效)
既然你要处理整个数组,不如只读取一次文件,在内存中完成所有数据更新,最后一次性写入——这样既避免了异步冲突,也减少了IO操作次数,效率更高。
代码示例:
const fs = require('fs').promises; async function writeAllData(arr) { try { // 只读取一次文件 const data = await fs.readFile('./assets/data/fb.json'); let newsData = JSON.parse(data); // 批量更新所有数据 arr.forEach(obj => { newsData[obj.link] = { ...newsData[obj.link], link: obj.link, title: obj.title, text: obj.contentSnippet, articleDate: obj.pubDate, content: obj.content }; }); // 一次性写入所有修改 await fs.writeFile("./assets/data/fb.json", JSON.stringify(newsData)); console.log('All 16 items have been written successfully!'); } catch (err) { console.error('Error processing batch data:', err); } } // 调用批量处理函数 writeAllData(arr);
这个方案是最优的,因为它只涉及一次读和一次写操作,完全规避了异步并发的问题。
内容的提问来源于stack exchange,提问作者user16614118
相关产品推荐
相关产品推荐

