Node.js使用fetch循环发请求时请求在for循环执行完毕后才触发问题咨询
问题原因
你看到的循环执行完成后才打印响应结果,是Node.js事件循环的固有执行逻辑,并不是请求等到循环结束才发送:
- 所有同步代码(for循环、
console.log(i)、checkbalance的同步执行部分)会优先在主线程执行完毕 - fetchUrl属于异步IO操作,它的回调函数会被加入任务队列,必须等主线程的同步代码全部执行完成后,才会被事件调度器取出执行
- 实际上请求在循环调用checkbalance的时候就已经发送出去了,只是响应回来后的回调被暂时挂起,等待同步代码跑完才执行
解决方案
基础版本(满足核心需求)
你当前的代码其实已经满足「循环内同步处理其他任务、无需等待请求返回、响应到达后异步处理」的需求,只需要修复var变量声明导致的作用域问题即可:
const fetchUrl = require("fetch").fetchUrl; // 把var改为let,使用块级作用域避免循环变量共享的问题 for (let i = 0; i < 10; i++) { console.log(i); // 此处可插入你需要同步处理的其他任务,不会等待请求返回 checkbalance(i); } function checkbalance(req) { const urlCheckBalance = "https://api.etherscan.io/api?module=account&action=balancemulti&address=" + req + "&tag=latest&apikey=<api key>"; fetchUrl(urlCheckBalance, function (error, meta, body) { // 响应返回后自动异步执行该回调,不会阻塞主线程任务 console.log(body.toString()); }); }
边循环边处理响应版本
如果你的循环内同步任务耗时较长,希望循环执行过程中就能处理已经返回的响应,可以通过setImmediate给事件循环留出回调处理时间:
const fetchUrl = require("fetch").fetchUrl; (async function runLoop() { for (let i = 0; i < 10; i++) { // 让出主线程,让已返回的请求回调可以执行 await new Promise(resolve => setImmediate(resolve)); console.log(i); // 同步处理其他任务 checkbalance(i); } })() function checkbalance(req) { const urlCheckBalance = "https://api.etherscan.io/api?module=account&action=balancemulti&address=" + req + "&tag=latest&apikey=<api key>"; fetchUrl(urlCheckBalance, function (error, meta, body) { console.log(`请求${req}返回结果:`, body.toString()); }); }
额外报错说明
你返回结果里的两类报错原因:
- 无效地址格式:etherscan的balancemulti接口需要传入合法的以太坊钱包地址,你直接传入数字i不符合接口参数要求
- 速率限制:短时间并发请求过多,超过了etherscan免费API的请求频率限制,可以适当加请求间隔或者调整并发数
内容的提问来源于stack exchange,提问作者Ramin Najafi
相关产品推荐
相关产品推荐

