JavaScript/Node.js新手求助:异步函数返回值异常问题
嘿,刚入门JS和Node.js碰到这个问题太正常了,我当初也踩过一模一样的坑!让我给你理清楚为啥会这样,还有怎么解决~
你遇到的核心问题是request.post是异步操作——当你调用f(args)的时候,函数f会立刻启动这个HTTP请求,但不会傻等着请求完成,而是直接跳转到函数末尾(因为回调函数是要等请求拿到响应后才会触发的)。而你的函数f本身没有明确的return语句(除了回调里的return,那只属于回调函数),所以默认返回undefined,这就是#1控制台打印undefined的原因。
而#2的console.log(JSON.parse(body))是写在request.post的回调里的,这个回调要等到HTTP请求真正完成、拿到服务器响应后才会执行,所以它能正常获取并解析body内容。
下面给你两种常用的解决方案,第二种是现在Node.js里更推荐的写法:
方法1:给函数f传入回调参数
既然异步操作的结果不能立刻拿到,我们可以让调用f的时候传一个回调函数,等请求完成后把结果传给这个回调:
function f(args, callback) { request.post({stuff}, function optionalCallback(error, response, body) { if (error) { console.log('upload failed:', error); return callback(error); // 把错误信息传给回调 } const parsedBody = JSON.parse(body); console.log(parsedBody); // #2 callback(null, parsedBody); // 把成功的结果传给回调 }); } // 后续switch语句里这样调用 case 'test': f(args, function(err, result) { if (err) { // 这里处理请求失败的情况 return; } console.log(result); // 这里就能拿到正确的解析结果了 }); break;
方法2:使用Promise + async/await(推荐,代码更易读)
现在Node.js里更推荐用Promise来处理异步逻辑,我们可以把f改成返回一个Promise,再用async/await语法来“同步化”异步代码:
首先改造函数f:
function f(args) { return new Promise((resolve, reject) => { request.post({stuff}, function optionalCallback(error, response, body) { if (error) { console.log('upload failed:', error); return reject(error); // 请求失败时,抛出错误 } const parsedBody = JSON.parse(body); console.log(parsedBody); // #2 resolve(parsedBody); // 请求成功时,返回结果 }); }); } // 在switch语句里使用async/await,注意要包在async函数里 case 'test': (async function() { try { const result = await f(args); console.log(result); // 这里就能拿到正确结果 } catch (err) { // 处理请求失败的错误 console.error(err); } })(); break;
如果你之后想简化代码,可以换成本身支持Promise的HTTP库(比如axios),代码会更简洁:
const axios = require('axios'); async function f(args) { try { const response = await axios.post('你的请求地址', {stuff}); const parsedBody = response.data; // axios会自动帮你解析JSON console.log(parsedBody); return parsedBody; } catch (error) { console.log('upload failed:', error); throw error; } } // switch里的调用方式和上面一致 case 'test': (async function() { try { console.log(await f(args)); } catch (err) { console.error(err); } })(); break;
注意:await只能在async标记的函数里使用,所以我们在case里用了一个立即执行的async函数,如果你整个包含switch的函数本身就是async函数,那就可以直接用await啦~
内容的提问来源于stack exchange,提问作者MathT

