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

Node.js中Promise {<pending>}问题及CSV写入失效求助

解决Node.js中Promise {}及CSV写入失败问题

问题根源分析

  1. 异步操作未正确等待:data事件的异步回调中,循环调用makeGraphQLRequest时未使用await,导致scrapInfo中存入的是未完成的Promise对象,且outputData提前被push,此时异步请求还未返回结果。
  2. 错误处理逻辑缺陷:原makeGraphQLRequest的重试逻辑未返回Promise,出错后无法让上层感知重试状态,也没有重试次数限制,可能导致无限递归。
  3. CSV写入时机错误:end事件触发时,所有异步请求还未完成,outputData数据不完整,且初始化csvWriter时未指定表头,导致写入失败。

修复后完整代码

const fs = require('fs');
const csv = require('csv-parser');
const axios = require('axios');
const { createObjectCsvWriter } = require('csv-writer');

console.time('ScriptExec');
let outputData = [];

// 必须指定CSV表头,否则无法正确写入
const csvWriter = createObjectCsvWriter({
    path: 'output.csv',
    header: [
        {id: 'title', title: 'title'},
        {id: 'prices', title: 'prices'}
    ]
});

// 修复重试逻辑:返回Promise、限制重试次数、传递请求参数
async function makeGraphQLRequest(apiUrl, query, variables, retryCount = 3) {
    try {
        const response = await axios.post(apiUrl, {
            query: query,
            variables: variables,
        });
        return response.data.data.price_trader_log
            .map(entry => entry.values[entry.values.length - 1].price_trader_new);
    } catch (error) {
        if (retryCount > 0) {
            console.log(`请求失败,剩余重试次数:${retryCount - 1},2秒后重试`);
            await new Promise(resolve => setTimeout(resolve, 2000));
            return makeGraphQLRequest(apiUrl, query, variables, retryCount - 1);
        }
        console.error('重试次数耗尽,请求失败:', error);
        throw error;
    }
}

const apiUrl = 'https://wiki.cs.money/api/graphql';
const query = `
query price_trader_log($name_ids: [Int!]!) {
  price_trader_log(input: {name_ids: $name_ids}) {
    name_id
    values {
      price_trader_new
    }
  }
}
`;

// 将CSV读取转为Promise,避免回调嵌套时序问题
async function readCsv() {
    return new Promise((resolve, reject) => {
        const rows = [];
        fs.createReadStream('variables.csv')
            .pipe(csv())
            .on('data', row => rows.push(row))
            .on('end', () => resolve(rows))
            .on('error', err => reject(err));
    });
}

// 主逻辑:串行处理所有行,等待每个请求完成后再处理下一行
async function main() {
    try {
        const rows = await readCsv();
        for (const row of rows) {
            const nameIds = row.name_ids.split(',').map(id => parseInt(id.trim()));
            const scrapInfo = [];
            console.log(nameIds);

            for (const pickId of nameIds) {
                console.log(pickId);
                if (pickId > 0) {
                    const variables = { name_ids: pickId };
                    try {
                        // 等待请求完成后再存入结果
                        const result = await makeGraphQLRequest(apiUrl, query, variables);
                        scrapInfo.push(result);
                    } catch (err) {
                        scrapInfo.push('获取失败'); // 自定义错误标记
                    }
                } else {
                    scrapInfo.push(pickId);
                }
            }
            // 将数组转为字符串,适配CSV单元格格式
            outputData.push({ title: row.title, prices: scrapInfo.join('|') });
            console.log(outputData);
        }
        // 所有数据处理完成后再写入CSV
        await csvWriter.writeRecords(outputData);
        console.log('数据成功写入output.csv');
    } catch (err) {
        console.error('处理过程出错:', err);
    } finally {
        console.timeEnd('ScriptExec');
    }
}

main();

核心修复说明

  • 异步时序控制:将CSV读取和请求处理逻辑封装到异步函数main中,用await确保所有请求完成后再写入CSV,彻底解决Promise {<pending>}问题。
  • 错误处理优化:给重试逻辑添加次数限制,重试时返回Promise,确保上层能等待重试结果;请求失败时抛出异常,方便上层做降级处理。
  • CSV写入修复:初始化csvWriter时指定表头,将scrapInfo数组转为字符串格式,避免CSV格式混乱,且确保所有数据处理完成后再执行写入操作。
  • 避免回调嵌套:将CSV读取转为Promise,消除回调嵌套导致的时序混乱问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 13:29:52