Node.js中Promise {<pending>}问题及CSV写入失效求助
解决Node.js中Promise {}及CSV写入失败问题
问题根源分析
- 异步操作未正确等待:
data事件的异步回调中,循环调用makeGraphQLRequest时未使用await,导致scrapInfo中存入的是未完成的Promise对象,且outputData提前被push,此时异步请求还未返回结果。 - 错误处理逻辑缺陷:原
makeGraphQLRequest的重试逻辑未返回Promise,出错后无法让上层感知重试状态,也没有重试次数限制,可能导致无限递归。 - 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
相关产品推荐
相关产品推荐

