假设我想在单个 JS 模块中导出一些通过调用某个异步函数获得的值。使导出等待结果/Promise 解决的机制是什么?
作为示例代码片段,我将其放在这里
function go() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("Success!"), 3000);
});
}
let AS;
go().then((x) => {
AS = x;
});
module.exports = AS;
该函数可以发出任何API请求。 我不想导出整个函数并在其他模块中调用它。
给你两个答案:
现在对 ESM 的支持已经成熟,在大多数 Node.js 项目中,您可以通过将
"type": "module"
放入 package.json
中并根据需要将 require()
调用更改为 import
语句,从 CommonJS 切换到 ESM。如果你能做到这一点,你可以使用 top-level await
。使用顶级 await
,您可以让模块执行等待承诺解决(这使得从模块导入的模块等待承诺解决)。所以你会这样做:
function go() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("Success!"), 500);
});
}
export const AS = await go();
导出命名导出,但您也可以用此替换最后一行以执行默认导出(这与 CommonJS 代码中的内容更相似;我更喜欢避免默认导出):
export default await go();
使用你的模块的模块不必知道该值来自异步源;在模块评估完成(承诺解决后)之前,它们不会被评估。他们只是像往常一样导入:
import { AS } from "./promise.js"; // If it's a named export
// import AS from "./promise.js"; // If it's the default export
console.log("AS = " + AS);
使用 CommonJS,最好的选择是导出承诺。这样,使用模块的代码就有了一种标准方法来处理该值可能尚不可用的事实 - 使用承诺:
require("./your-moudule")
.then(AS => {
// ...use `AS` here...
})
.catch(error => {
// ...handle the fact we didn't get it here...
});
但是如果您想导出该值,也可以,但这通常不是您的最佳方法。您可以通过导出一个对象然后更新其
AS
属性来做到这一点:
function go() {
return new Promise((resolve, reject) => {
setTimeout(() => resolve("Success!"), 500);
});
}
module.exports = {AS: undefined};
go().then((x) => {
module.exports.AS = x;
});
使用您的模块的模块必须处理这样一个事实:在进程启动期间短暂(可能),它们将得到
undefined
。这是使用上面模块的代码:
const mod = require("./promise"); // Note: Not destructuring at this point
const timer = setInterval(() => {
const { AS } = mod;
console.log("AS = " + AS);
if (AS) {
clearInterval(timer);
}
}, 100);
如果运行该命令,您会看到
AS = undefined
~5 次,然后是 AS = Success!
。
在这两种情况下,它都会使代码使用你的模块变得更加复杂(当他们看到
undefined
时,他们必须等待承诺或循环)。从这个角度来看,ESM 的方法更简单(这些模块在您的模块准备好之前不会运行)。