You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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完全一致,最后拼接写入就不会乱序。
  • 缺点:如果文件很多且很大,所有行都会存在内存里,可能会占用较多内存,所以适合小文件场景。

额外注意事项

  1. 错误处理:如果某个JSON文件解析失败,上面的代码会中断整个流程。你可以在循环或map里加局部try/catch,跳过出错的文件:
    // 串行方案里的循环内加try/catch
    for (const jsonPath of jsonFiles) {
      try {
        // 读取、解析、写入逻辑
      } catch (fileErr) {
        console.error(`⚠️ 跳过出错的文件 ${jsonPath}:`, fileErr);
        continue;
      }
    }
    
  2. 文件路径:确保jsonFiles里的路径是正确的,用path.join拼接绝对路径避免相对路径问题。

内容的提问来源于stack exchange,提问作者Sineos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:43:06