如何基于异步数据读写CSV?解决API调用未完成提前写入问题
解决CSV流处理中API调用未完成就写入文件的问题
你的问题出在Node.js流的事件机制上:data事件的回调是异步函数,但end事件会在所有数据块被推送后立即触发,完全不会等待回调里的externalAPI调用完成。最后一行数据触发data事件后,流就直接触发end开始写文件了,此时最后一次API请求还没返回,自然就少了最后一行。
下面是两种靠谱的修复方案:
方案一:用异步迭代器顺序处理(代码简洁,适合中小文件)
直接用for await...of遍历CSV流,确保每一行的API调用完成后再处理下一行,所有行处理完再写入文件:
const fs = require('fs'); const { parse, stringify } = require('csv'); async function processCSV() { const data = []; // 异步迭代器遍历所有行 for await (const row of fs.createReadStream('./foo.csv').pipe(parse({ delimiter: ',', from_line: 2 }))) { const foo1 = row[0]; const foo2 = await externalAPI(row[1]); data.push([foo1, foo2]); } // 所有行处理完毕后再写入 const output = stringify(data); fs.writeFileSync('demoA.csv', output); } // 执行并捕获错误 processCSV().catch(err => console.error(err));
方案二:用Transform流式处理(低内存占用,适合大文件)
如果你的CSV文件很大,不想把所有数据存在内存里,可以用transform流来逐行处理,最后直接管道到写入流,finish事件会在所有数据处理并写入完成后才触发:
const fs = require('fs'); const { parse, transform, stringify } = require('csv'); fs.createReadStream('./foo.csv') .pipe(parse({ delimiter: ',', from_line: 2 })) // 异步转换每一行 .pipe(transform(async (row, callback) => { try { const foo1 = row[0]; const foo2 = await externalAPI(row[1]); callback(null, [foo1, foo2]); } catch (err) { callback(err); } })) .pipe(stringify()) .pipe(fs.createWriteStream('demoA.csv')) .on('finish', () => { console.log('所有数据处理并写入完成'); }) .on('error', (err) => { console.error(err.message); });
为什么原来的代码不行?
你用的pause()和resume()只能控制流的读取速度,但流的end事件触发逻辑是:当底层数据源没有更多数据可以推送时,就会触发end,完全不关心data回调里的异步操作是否完成。async函数的Promise不会被流的事件系统追踪,所以最后一行的API调用还在pending时,end就已经执行写文件操作了。
内容的提问来源于stack exchange,提问作者Xavier
相关产品推荐
相关产品推荐

