Node.js按指定顺序处理JSON生成有序CSV的Promise实现问题
我太懂你这个痛点了——几百个JSON要按指定顺序转CSV,结果用Promise.all搞出来的顺序乱七八糟,试了各种组合要么Promise pending要么顺序还是错,简直头大!别担心,咱们直接抓核心问题解决:Promise.all是并行执行的,谁先处理完谁先输出,完全不按你给的文件数组顺序来。下面给你两种靠谱的实现方案,按需选就行。
方案一:串行处理(内存友好,绝对保序)
这种方法是一个文件处理完、写入CSV后,再处理下一个,完美贴合你要的「按指定顺序处理+写入」需求,而且不用一次性加载所有JSON到内存,适合几百个文件的场景。
代码实现
const fs = require('fs').promises; const path = require('path'); // 1. 配置你的CSV头和待处理的JSON文件数组(顺序完全按你需要的来) const csvHeader = 'id,name,description\n'; const jsonFiles = ['user_001.json', 'user_002.json', 'user_003.json']; // 替换成你的文件列表 const outputCsv = path.join(__dirname, 'result.csv'); async function processInOrder() { try { // 先写入CSV头(覆盖已有内容,确保每次运行都是新文件) await fs.writeFile(outputCsv, csvHeader, 'utf8'); // 2. 串行遍历每个JSON文件,强制按顺序处理 for (const jsonPath of jsonFiles) { // 读取并解析JSON const rawContent = await fs.readFile(path.join(__dirname, jsonPath), 'utf8'); const jsonData = JSON.parse(rawContent); // 3. 提取你需要的字段,注意处理CSV转义(比如字段含逗号/引号的情况) const escapedName = jsonData.name.replace(/"/g, '""'); // 双引号转义 const csvRow = `"${jsonData.id}","${escapedName}","${jsonData.description}"\n`; // 4. 追加到CSV文件 await fs.appendFile(outputCsv, csvRow, 'utf8'); } console.log('✅ 所有文件处理完成!CSV顺序和JSON文件数组完全一致'); } catch (err) { console.error('❌ 处理出错:', err); } } processInOrder();
关键说明
for...of+await是串行执行的核心:每次循环都会等当前文件的读取、解析、写入操作全部完成,才会进入下一个文件的处理,绝对不会乱序。- 加入了CSV转义逻辑:如果字段里有逗号、双引号,直接写会破坏CSV格式,用
replace(/"/g, '""')把双引号转成两个双引号是CSV的标准转义方式。
方案二:并行收集结果,再按顺序写入(速度更快,适合小文件)
如果你觉得串行处理速度慢,且JSON文件都不大,可以先并行读取所有文件、收集结果(Promise.all会保证结果数组的顺序和输入数组一致),最后一次性写入CSV。
代码实现
const fs = require('fs').promises; const path = require('path'); const csvHeader = 'id,name,description\n'; const jsonFiles = ['user_001.json', 'user_002.json', 'user_003.json']; const outputCsv = path.join(__dirname, 'result.csv'); async function processAndWriteInOrder() { try { // 1. 并行处理所有JSON,得到按顺序的CSV行数组 const csvRows = await Promise.all( jsonFiles.map(async (jsonPath) => { const rawContent = await fs.readFile(path.join(__dirname, jsonPath), 'utf8'); const jsonData = JSON.parse(rawContent); // 同样处理转义 const escapedName = jsonData.name.replace(/"/g, '""'); return `"${jsonData.id}","${escapedName}","${jsonData.description}"\n`; }) ); // 2. 一次性写入CSV头+所有行(顺序完全和JSON数组一致) await fs.writeFile(outputCsv, csvHeader + csvRows.join(''), 'utf8'); console.log('✅ 并行处理完成!CSV顺序正确'); } catch (err) { console.error('❌ 处理出错:', err); } } processAndWriteInOrder();
关键说明
- Promise.all的特性:虽然内部是并行执行,但返回的结果数组顺序严格对应输入的Promise数组顺序,所以csvRows的顺序和jsonFiles完全一致,最后拼接写入就不会乱序。
- 缺点:如果文件很多且很大,所有行都会存在内存里,可能会占用较多内存,所以适合小文件场景。
额外注意事项
- 错误处理:如果某个JSON文件解析失败,上面的代码会中断整个流程。你可以在循环或map里加局部try/catch,跳过出错的文件:
// 串行方案里的循环内加try/catch for (const jsonPath of jsonFiles) { try { // 读取、解析、写入逻辑 } catch (fileErr) { console.error(`⚠️ 跳过出错的文件 ${jsonPath}:`, fileErr); continue; } } - 文件路径:确保jsonFiles里的路径是正确的,用
path.join拼接绝对路径避免相对路径问题。
内容的提问来源于stack exchange,提问作者Sineos
相关产品推荐
相关产品推荐

