Node.js循环中按顺序执行REST API调用函数的问题求助
问题分析与修复方案
核心问题点
你的代码出现执行顺序混乱、数据丢失的问题,根源在于以下几个异步编程的常见错误:
var声明导致的变量作用域泄漏:循环中用var obj,变量会被提升到main函数作用域,所有异步函数最终引用的都是循环最后一次的obj值。- 异步函数未正确返回Promise:
createDocument内部的fetch是异步操作,但函数直接return,没有等待请求完成,await createDocument()无法起到等待效果。 - 未等待异步函数完成就进入下一次循环:循环中直接调用
updatePublication(),没有加await,导致所有迭代的API请求并行触发,完全打乱执行顺序。 - 共享变量
id引发竞态条件:多个异步操作同时修改全局的id,后续的updatePublication拿到的不是对应createDocument返回的ID。 - 缺乏错误处理:
fetch请求失败时没有捕获异常,无法排查请求失败的原因。
修复后的完整代码
const csv = require("csvtojson"); const jq = require('node-jq'); const parentFolder = 1234; const token = ""; const csvFilePath = 'licenses.tsv'; const parentPublicationId = 5678; // 封装createDocument为返回Promise的函数,直接返回创建的文档ID async function createDocument(name) { const content = `<title>${name}</title></section>`; const inputBody = { "parent": parentFolder, "name": name, "content": content, "subtype": "component" }; const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Basic ' + token, }; const res = await fetch('https://thesite.com/api/v2/documents', { method: 'POST', body: JSON.stringify(inputBody), headers: headers }); if (!res.ok) { throw new Error(`创建文档失败:${res.status} ${res.statusText}`); } const body = await res.json(); console.log('创建文档响应:', body); return body.id; } // 封装updatePublication,接收文档ID作为参数,避免共享变量 async function updatePublication(documentId) { const inputBody = { "parent": parentPublicationId, "document": documentId, "position": null, // 这里应该用null而不是字符串"null" }; const headers = { 'Content-Type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Basic ' + token, }; const res = await fetch('https://thesite.com/api/v2/forks', { method: 'POST', headers: headers, body: JSON.stringify(inputBody) }); if (!res.ok) { throw new Error(`更新出版物失败:${res.status} ${res.statusText}`); } const body = await res.json(); console.log('更新出版物响应:', body); } async function main() { try { // 简化TSV转JSON的流程,直接用await接收结果 const jsonObj = await csv({ delimiter: '\t' }).fromFile(csvFilePath); // 简化jq处理流程,直接接收结果并解析 const jsonOutput = await jq.run('[.[] | {name: .sourceName}]', jsonObj, { input: 'json' }); const parsed = JSON.parse(jsonOutput); // 用for...of循环确保每次迭代的变量独立,且顺序执行 for (const item of parsed) { console.log(`开始处理:${item.name}`); const docId = await createDocument(item.name); await updatePublication(docId); console.log(`完成处理:${item.name}`); } console.log('所有任务执行完成'); } catch (err) { console.error('执行出错:', err); process.exit(1); } } main();
关键修改说明
- 提取异步函数到外部:把
createDocument和updatePublication从循环中提取出来,避免每次循环重新创建函数,同时让逻辑更清晰。 - 异步函数返回Promise并传递参数:
createDocument直接返回创建成功的文档ID,updatePublication接收该ID作为参数,彻底避免共享变量的竞态问题。 - 使用
for...of循环:替代for...in+var,确保每次迭代的变量item是独立的,不会被后续迭代覆盖。 - 循环中添加
await:每次迭代都等待createDocument和updatePublication完成后再进入下一次,保证执行顺序严格按CSV行顺序进行。 - 完善错误处理:在
fetch后检查响应状态,用try/catch包裹整个流程,方便排查错误。 - 简化TSV转JSON和jq处理:直接用
await接收库的返回结果,去掉冗余的中间变量。
内容的提问来源于stack exchange,提问作者hcdocs
相关产品推荐
相关产品推荐

