如何让JavaScript函数返回异步调用获取的值?
解决异步函数返回值的问题
你的问题核心就是JavaScript异步操作的执行顺序导致的——getNumPages里的同步代码会先跑完,而makeRequest的回调要等异步请求完成后才执行,所以return的时候totalPages还是初始值0。要让getNumPages能返回异步获取到的页数,得用Promise或者async/await来处理,下面给你几种可行的方案:
方案1:让getNumPages返回Promise
把getNumPages改成返回一个Promise,在makeRequest的回调里resolve结果:
const https = require('https'); function getNumPages(substr) { return new Promise((resolve) => { console.log(`Before makeRequest`); makeRequest(substr, (numPages) => { console.log(`Inside callback from makeRequest: ${numPages}`); // 异步操作完成后,把结果传递出去 resolve(numPages); }); }); } // 调用方式:用then获取结果 getNumPages('Shakespeare') .then(totalPages => { console.log(`最终页数:${totalPages}`); // 这里就能拿到正确的79了 });
如果makeRequest可能出现错误(比如请求失败),记得加上reject来处理错误:
function getNumPages(substr) { return new Promise((resolve, reject) => { makeRequest(substr, (numPages, err) => { if (err) { reject(err); return; } resolve(numPages); }); }); } // 调用时加上catch处理错误 getNumPages('Shakespeare') .then(totalPages => console.log(totalPages)) .catch(err => console.error('请求出错:', err));
方案2:用async/await简化代码
async/await是Promise的语法糖,写起来更像同步代码。首先需要把makeRequest包装成返回Promise的函数,或者直接改造它:
第一步:包装makeRequest为Promise版本
function makeRequestPromise(substr) { return new Promise((resolve, reject) => { makeRequest(substr, (numPages, err) => { if (err) reject(err); else resolve(numPages); }); }); }
第二步:把getNumPages改成async函数
async function getNumPages(substr) { console.log(`Before makeRequest`); // 用await等待Promise完成,拿到结果 const totalPages = await makeRequestPromise(substr); console.log(`After makeRequest: ${totalPages}`); return totalPages; }
调用方式
因为async函数返回的还是Promise,所以可以在另一个async函数里用await,或者用then:
// 顶层await需要在async IIFE里(Node.js环境支持) (async () => { try { const pages = await getNumPages('Shakespeare'); console.log(pages); // 79 } catch (err) { console.error(err); } })();
为什么原来的代码不行?
简单说,JavaScript是单线程的,异步操作(比如你的https请求)会被放到事件队列里,当前的同步代码(console.log('After makeRequest')和return totalPages)会先执行完毕,等异步请求完成后,才会从事件队列里取出回调函数执行。所以return的时候,totalPages还没被赋值,自然返回0。
进阶:直接改造makeRequest为Promise版本
如果makeRequest是你自己写的,最好直接让它返回Promise,避免额外的包装:
function makeRequest(substr) { return new Promise((resolve, reject) => { // 这里写你的https请求逻辑,比如: const url = `https://your-api-url?substr=${encodeURIComponent(substr)}`; https.get(url, (res) => { let rawData = ''; res.on('data', (chunk) => { rawData += chunk; }); res.on('end', () => { try { const result = JSON.parse(rawData); const numPages = result.totalPages; // 假设API返回的结构里有totalPages resolve(numPages); } catch (e) { reject(e); } }); }).on('error', (e) => { reject(e); }); }); }
这样getNumPages就可以直接用await调用它,代码更简洁。
内容的提问来源于stack exchange,提问作者sco
相关产品推荐
相关产品推荐

