如何导出then()回调中的值?Node.js模块导出异步值返回空对象问题
空对象问题的核心原因
Node.js 加载 CommonJS 模块时,会 同步执行完模块顶层的所有同步代码就立刻完成导出流程。
你原代码里的got()是异步HTTP请求,执行到这行时模块不会等待请求返回的then回调逻辑跑完,就直接把当时的初始导出对象(默认是空对象{})返回给require()的调用方。等后面异步请求拿到结果、在最后一个then回调里执行module.exports = out时,外部require拿到的模块引用早就定型,根本接收不到后续的赋值,自然打印出来是空对象。
另外原代码里引入的fs模块全程没有使用,属于冗余依赖可以直接删掉。
推荐解决方案
方案1:导出Promise本身,由调用方异步取值(最常用)
不要在模块内部强行把异步结果转成同步导出,直接把整个请求处理的Promise链作为模块的导出值,由调用方自行等待异步结果:
模块文件示例:
const got = require('got'); const jsdom = require("jsdom"); const { JSDOM } = jsdom; const vgmUrl = 'https://example.com/somehtml.html'; // 直接把整个请求处理的Promise作为导出内容 module.exports = got(vgmUrl).then(response => { const dom = new JSDOM(response.body); const textContent = dom.window.document.getElementById("__NEXT_DATA__").textContent; const JSONData = JSON.parse(textContent); return JSONData.props.pageProps.text.vaccinaties.data.kpi_total.tab_total_estimated.value; });
引入模块时的写法:
// 拿到的导出值本身是一个Promise,不能直接打印,需要异步等待结果 const dataPromise = require('./module.js'); // 写法1:用then处理返回结果 dataPromise.then(out => { console.log(out); }); // 写法2:用async/await等待结果 async function printData() { const out = await dataPromise; console.log(out); } printData();
方案2:固定导出对象引用,异步更新对象属性
如果希望外部拿到一个固定引用的模块对象,等异步数据加载完成后更新对象内容,就不要在异步回调里直接给module.exports赋新值,而是修改导出对象的属性:
模块文件示例:
const got = require('got'); const jsdom = require("jsdom"); const { JSDOM } = jsdom; const vgmUrl = 'https://example.com/somehtml.html'; // 初始化固定结构的导出对象 module.exports = { data: null, isLoaded: false, // 预留注册回调的方法,数据加载完成后自动触发通知 onDataReady: (callback) => { if (module.exports.isLoaded) { callback(module.exports.data); } else { module.exports._readyCallback = callback; } } }; got(vgmUrl).then(response => { const dom = new JSDOM(response.body); const textContent = dom.window.document.getElementById("__NEXT_DATA__").textContent; const JSONData = JSON.parse(textContent); const result = JSONData.props.pageProps.text.vaccinaties.data.kpi_total.tab_total_estimated.value; // 只修改导出对象的属性,不重新赋值module.exports本身 module.exports.data = result; module.exports.isLoaded = true; // 触发预先注册的回调 if (module.exports._readyCallback) { module.exports._readyCallback(result); } });
引入模块时的写法:
const dataModule = require('./module.js'); // 注册数据加载完成的回调 dataModule.onDataReady(out => { console.log('拿到的目标数值:', out); });
避坑提醒
- 禁止在异步回调里直接执行
module.exports = 新值:CommonJS模块加载逻辑中,require()拿到的是模块导出初始值的引用,后续在异步流程里把module.exports指向新的内容,外部引用还是原来的空对象,完全感知不到变化。 - 不要试图在模块顶层同步等待HTTP请求的结果:这类网络请求本身是异步流程,强行同步阻塞会卡住Node.js的事件循环,导致整个服务失去响应。
内容的提问来源于stack exchange,提问作者Bastiaan Buitelaar
相关产品推荐
相关产品推荐

