【发布时间】:2020-05-13 10:17:18
【问题描述】:
好的,所以我有一种情况,我不能只向 API 服务器发出数千个请求。 我有一个 Node 进程(没有 UI),我需要依次处理每个 API 响应/更新,等待完成,然后再发送下一个请求。 我可能会让这比我想象的更复杂 - 不确定。我只能弄清楚如何通过递归调用来做到这一点,但这会导致堆栈溢出,因为可能有数千条记录。大致流程是这样的:
- 从带有 ID 的 SQL 表中获取行(结果)
- 制定并发送 API 调用以检索 ID 信息
- 如果返回的数据有图片数据,则回写到SQL表中
- 等待此过程,以免一次用数千个请求轰炸 API 服务器
- 重复直到处理完最后一个 ID(可以是数千,超过堆栈空间)
这里是示例代码(不是实际的,所以忽略语法错误,如果有的话)... 更新:删除敏感项目的实际运行代码
var g_con = null; //...yeah I know, globals are bad
//
// [ found updating ]
//
function getSetImage(result, row, found) {
if(row >= result.length) { //...exit on no row or last row processed
con.end();
return;
}
item = result[row]; //...next SQL row
if((item !== undefined) && (item.autoid !== undefined)) {
//...assemble API and send request
//
let url = 'https://...API header...'
+ item.autoid
+ '...API params...';
request(url, (error, response, body) => {
if(response.statusCode !== 200)
throw('Server is not responding\n' + response.statusMessage);
let imageData = JSON.parse(body);
if((imageData.value[0] !== undefined) &&
(imageData.value[0].DETAIL !== undefined) &&
(imageData.value[0].DETAIL.Value.length) ) {
//...post back to SQL
//
found++;
console.log('\n' + item.autoid + '/['+ item.descr + '], ' + 'Found:' + found);
qry = 'update inventory set image = "'+imageData.value[0].DETAIL.Value+'" where autoid = "'+item.autoid+'";';
g_con.query(qry, (err) => {
if (err) {
console.log('ERROR:',err.message, '\nSQL:['+err.sql+']\n');
throw err.message;
}
});
row++;
setTimeout(()=>{getSetImage(result, row, found)}, 0); //...nested call after SQL
} else {
row++;
process.stdout.write('.'); //...show '.' for record, but no image
setTimeout(()=>{getSetImage(result, row, found)}, 0); //...nested call after SQL
}
}); //...request callback
}
// } else {
// throw '\nERROR! result['+row+'] undefined? Images found: '+found;
// }
}
//
// [ main lines ]
//
(() => {
let params = null;
try {
params = JSON.parse(fs.readFileSync('./config.json'));
//...load autoids array from SQL inventory table - saving autoids
// autoids in INVENTRY join on par_aid's in INVENTRYIMAGES
//
g_con = mysql.createConnection(params.SQLConnection);
g_con.connect((err) => { if(err) {
console.log('ERROR:',err.message);
throw err.message;
}
});
//...do requested query and return data or an error
//
let qry = 'select autoid, descr from inventory order by autoid;';
g_con.query(qry, (err, results, flds) => {
if (err || flds === undefined) {
console.log('ERROR:',err.message, '\nSQL:['+err.sql+']\n');
throw err.message;
}
console.log('Results length:',results.length);
let row = 0;
let found = 0;
getSetImage(results, row, found);
});
}
catch (err) {
console.log('Error parsing config parameters!');
console.log(err);
}
})();
所以这是使用 Promises 的答案(MySQL 除外):
//
// [ found updating ]
//
async function getSetImage(data) {
for(let item of data) {
if(item && item.autoid) {
//...assemble API and send request
//
let url = g_URLHeader + g_URLPartA + item.autoid + g_URLPartB;
let image = await got(url).json().catch(err => {
console.log(err);
err.message = 'API server is not responding';
throw err;
});
if(image && image.value[0] && image.value[0].DETAIL &&
image.value[0].DETAIL.Value.length ) {
console.log('\nFound: ['+item.autoid+' - '+item.descr
+ '] a total of ' + g_found + ' in ' + g_count + ' rows');
g_found++;
//...post back to SQL
//
let qry = 'update inventory set image = "'
+ image.value[0].DETAIL.Value
+ '" where autoid = "'
+ item.autoid+'";';
await g_con.query(qry, (err) => {
if (err) {
console.log('ERROR:',err.message, '\nSQL:['+err.sql+']\n');
throw err.message;
}
});
} else {
process.stdout.write('.'); //...show '.' for record, but no image
} //...if/else image.value
g_count++;
} //...if item
} //...for()
}
【问题讨论】:
-
好吧,如果您从异步回调中递归调用该函数(看起来就是这样),这不会导致堆栈溢出。调用异步回调时,堆栈为空且已完全展开。仅供参考,如果您在所有内容(请求库和数据库)上使用 Promise 接口,然后使用
async/await,这将是一个干净的工具。您实际上可以只使用带有await的for循环,并且所有内容都可以在一个传统外观for循环中很好地序列化。 -
这段代码真的很想用promises和
async/await重写。不会有递归,而且会简单很多。如果您打算在 nodejs 中编程任何时间,那么学习如何使用 Promise 和async/await是完全值得的。在这段代码中,您可以切换到request-promise(尽管我个人更喜欢got()库,因为request系列处于维护模式,不再获得新功能)。然后,为您的数据库(内置)切换 promise 接口。 -
我不确定这是否有帮助,但每次我遇到其中一种情况时,都是因为我认为是异步的代码中的某些内容实际上不是异步的。您是否尝试过添加 processNextTick?
-
@kgingeri 将一些东西包装在一个 processNextTick 中并看看它是否仍然爆炸似乎不到一分钟的工作。如果没有,那么你已经学到了一些东西。如果您正在处理纯粹的同步调用,那么您应该以不同的方式处理问题。祝你好运!
-
好吧,在你这样做之前,我用
async/await创建了一个结构的轮廓。希望你能从中学习。
标签: javascript node.js recursion stack