JavaScript异步请求中变量赋值时机错误致响应异常的解决
问题:异步操作导致数组未填充就返回响应的解决办法
我初始化了数组dataarr,需要把XML转JSON后的数据push进去,而且是在循环里收集数据。但我在异步操作的作用域外返回响应时,数组还没填充完整,一开始误以为是作用域问题,请问该怎么解决?
最小示例代码
{... dataarr.push(jsonObj['soap:Envelope']['soap:Body']['ns:Response']['return']['items']); } // scope 1 结束 res.status(200).send(dataarr); // 在scope 2中需向客户端返回响应
完整代码
let dataarr = []; let payloadarr = []; ... axios(config) .then(function (response) { logger.log('info', 'POST /getdata successful from ' + req.ip); var options = { attributeNamePrefix : "@_", attrNodeName: "attr", //default is 'false' textNodeName : "#text", ignoreAttributes : true, ignoreNameSpace : false, allowBooleanAttributes : false, parseNodeValue : true, parseAttributeValue : false, trimValues: true, cdataTagName: "__cdata", //default is 'false' cdataPositionChar: "\\c", parseTrueNumberOnly: false, arrayMode: false, //"strict" attrValueProcessor: (val, attrName) => he.decode(val, {isAttributeValue: true}),//default is a=>a tagValueProcessor : (val, tagName) => he.decode(val), //default is a=>a stopNodes: ["parse-me-as-string"] }; console.log("response.data:"); console.log(response.data); if( parser.validate(response.data) === true) { //optional (it'll return an object in case it's not valid) var jsonObj = parser.parse(response.data,options); } var tObj = parser.getTraversalObj(response.data,options); var jsonObj = parser.convertToJson(tObj,options); console.log("jsonObj in parsing:"); console.log(jsonObj); console.log(jsonObj['soap:Envelope']['soap:Body']['ns:getResponse']['return']['items']); dataarr.push(jsonObj['soap:Envelope']['soap:Body']['ns:getResponse']['return']['items']); console.log("dataarr getting filled:"); console.log(j); console.log(dataarr); }) .catch(function (error) { logger.warn('[/getdocumentmetadata]: ', new Error(error)); //console.log(error); }); } }) .catch(function (error) { logger.warn('[GET /getdocuments]: ', new Error(error)); }); res.status(200).send(dataarr); });
解决方案
这不是作用域问题,是异步操作的执行顺序导致的:axios请求是异步任务,代码会先执行到res.status(200).send(dataarr),此时then回调里的XML解析、数组填充逻辑还未执行,所以返回的是空数组。
解决方法一:将响应逻辑移到then回调内部
确保数据填充完成后再返回响应,直接把res.send放到then的末尾:
axios(config) .then(function (response) { // ... XML转JSON、数据处理代码 ... dataarr.push(jsonObj['soap:Envelope']['soap:Body']['ns:getResponse']['return']['items']); // 数据准备完成后返回响应 res.status(200).send(dataarr); }) .catch(function (error) { logger.warn('[/getdocumentmetadata]: ', new Error(error)); // 错误场景也要返回响应,避免客户端等待超时 res.status(500).send({ error: '数据获取失败' }); }); // 删除外部的res.status(200).send(dataarr);
解决方法二:用async/await简化异步流程
将外层路由处理函数标记为async,用await等待axios请求完成,让代码按同步逻辑执行:
// 假设这是Express路由处理函数,添加async关键字 app.get('/your-api-path', async function(req, res) { let dataarr = []; let payloadarr = []; ... try { // 等待axios请求完成 const response = await axios(config); logger.log('info', 'POST /getdata successful from ' + req.ip); // ... XML转JSON、数据处理代码 ... var jsonObj = parser.convertToJson(tObj, options); dataarr.push(jsonObj['soap:Envelope']['soap:Body']['ns:getResponse']['return']['items']); // 数据填充完成后返回响应 res.status(200).send(dataarr); } catch(error) { logger.warn('[GET /getdocuments]: ', new Error(error)); res.status(500).send({ error: '请求处理失败' }); } });
额外注意
如果是循环中多次调用axios,需要用Promise.all等待所有异步请求完成后再处理数组:
// 示例:循环生成多个axios请求Promise const requestPromises = payloadarr.map(item => axios(getConfig(item))); // 等待所有请求完成 const responses = await Promise.all(requestPromises); // 遍历响应填充数组 responses.forEach(response => { const jsonObj = parser.convertToJson(parser.getTraversalObj(response.data, options), options); dataarr.push(jsonObj['soap:Envelope']['soap:Body']['ns:getResponse']['return']['items']); }); // 最后返回响应 res.status(200).send(dataarr);
内容的提问来源于stack exchange,提问作者Munchkin
相关产品推荐
相关产品推荐

