Node.js中request包调用API后全局变量未更新问题求助
解决Node.js中request异步调用导致全局变量无法更新的问题
这问题我见得挺多的,核心就是request的调用是异步操作,JavaScript的执行顺序在这会给你挖个小坑。你看,你的代码里,声明完全局变量后就发起了两个request请求,但这些请求不会阻塞后续代码执行——Node.js会先把同步代码跑完,等请求拿到响应了才会回头执行回调里的赋值逻辑。所以当你在回调外部尝试访问contractAddress或abi时,它们还没被赋值,自然是undefined。
给你捋捋代码的实际执行顺序:
- 声明
contractAddress和abi,初始值都是undefined - 发起第一个request请求(丢到异步任务队列,继续往下走)
- 发起第二个request请求(同样丢到任务队列)
- 如果这时候你有代码去读这两个变量,拿到的肯定是
undefined - 等两个请求都收到响应了,才会依次执行对应的回调,给变量赋值
下面给你几个可行的解决方案,从传统到现代的都有,你按需选:
方案1:嵌套回调(最直接的写法,适合有依赖的请求)
如果两个请求有先后顺序(比如得先拿到地址才能拿ABI),直接把第二个请求放到第一个的回调里就行,这样能保证赋值顺序:
var request = require('request'); // 尽量别用全局变量,后续逻辑封装成函数更靠谱 function handleContractData(contractAddress, abi) { console.log("拿到合约地址:", contractAddress); console.log("拿到合约ABI:", abi); // 在这里写你的业务逻辑 } request('http://expressanthem:9000/getcontractAddress', function(error, response, body) { if (error || response.statusCode !== 200) { console.error("获取合约地址失败:", error || `状态码${response.statusCode}`); return; } const contractAddress = body; console.log(`${contractAddress} contract address at first`); // 拿到地址后再请求ABI request('http://expressanthem:9000/getcontractAbi', function(error, response, body1) { if (error || response.statusCode !== 200) { console.error("获取合约ABI失败:", error || `状态码${response.statusCode}`); return; } const abi = body1; // 两个数据都拿到了,执行后续逻辑 handleContractData(contractAddress, abi); }); });
方案2:用Promise封装(更优雅的异步处理,支持并行请求)
如果两个请求没有依赖,可以同时发起,用Promise.all等待两个请求都完成,这样效率更高:
var request = require('request'); // 把request包装成Promise,方便后续用Promise语法 function requestWrapper(url) { return new Promise((resolve, reject) => { request(url, (error, response, body) => { if (error || response.statusCode !== 200) { reject(error || new Error(`请求失败,状态码:${response.statusCode}`)); } else { resolve(body); } }); }); } // 并行发起两个请求 Promise.all([ requestWrapper('http://expressanthem:9000/getcontractAddress'), requestWrapper('http://expressanthem:9000/getcontractAbi') ]) .then(([contractAddress, abi]) => { // 两个请求都成功返回,这里拿到所有数据 console.log("合约地址:", contractAddress); console.log("合约ABI:", abi); handleContractData(contractAddress, abi); }) .catch(err => { console.error("请求出错:", err); }); function handleContractData(address, abiData) { // 业务逻辑写这 }
方案3:async/await(最简洁的现代写法,可读性拉满)
基于Promise的封装,用async/await让异步代码看起来像同步代码,写起来更舒服:
var request = require('request'); function requestWrapper(url) { return new Promise((resolve, reject) => { request(url, (error, response, body) => { if (error || response.statusCode !== 200) { reject(error || new Error(`请求失败,状态码:${response.statusCode}`)); } else { resolve(body); } }); }); } // 用async函数包裹异步逻辑 async function fetchAndHandleData() { try { // 并行请求两个接口 const [contractAddress, abi] = await Promise.all([ requestWrapper('http://expressanthem:9000/getcontractAddress'), requestWrapper('http://expressanthem:9000/getcontractAbi') ]); console.log("合约地址:", contractAddress); console.log("合约ABI:", abi); handleContractData(contractAddress, abi); } catch (err) { console.error("获取数据失败:", err); } } // 调用这个async函数 fetchAndHandleData(); function handleContractData(address, abiData) { // 你的业务逻辑 }
最后提个小建议:尽量别用全局变量传递异步获取的数据,像上面那样把后续逻辑封装成函数,通过参数传递数据,代码会更清晰,也不容易出现变量污染或者时序问题。另外,一定要处理错误情况,不然遇到网络波动或者接口挂了,你连错在哪都不知道。
内容的提问来源于stack exchange,提问作者Jithin Zacharia
相关产品推荐
相关产品推荐

