背景
我正在从 AWS Secrets Manager 返回数据并使用 aws-sdk
执行此操作。早些时候我问了一个关于如何正确返回数据并将其导出的问题,因为导出的对象在导出到其他地方时从未解析过数据。这导致我得到了一堆未定义的。
解决该问题后,确定处理此问题的方法是将 aws-sdk 函数包装在一个 promise 中,然后在另一个文件中使用 async await 调用该 promise。这给我带来了问题。
示例
如果我像这样从 AWS 请求并返回数据,
let secrets = {
jwtHash: 10,
};
const client = new AWS.SecretsManager({
region: region
});
const promise = new Promise((resolve, reject) => {
client.getSecretValue({ SecretId: secretName }, async (err, data) => {
if (err) {
reject(err);
} else {
const res = await JSON.parse(data.SecretString);
secrets.dbUsername = res.username;
secrets.dbPassword = res.password;
secrets.dbHost = res.host;
secrets.dbPort = res.port;
secrets.dbDatabase = res.dbname;
resolve(secrets);
}
});
});
module.exports = promise;
然后我可以将它导入另一个文件并使用这样的数据,
const promise = require('../secrets');
(async () => {
const secrets = await promise;
// use secrets here
})();
现在让我们说在我尝试使用 secret 的那个文件中我有这样的东西,
const pool = new Pool({
user: secrets.dbUsername,
host: secrets.dbHost,
database: secrets.dbDatabase,
password: secrets.dbPassword,
port: secrets.dbPort
});
pool.on('error', err => {
console.error('Unexpected error on idle client', err);
process.exit(-1);
});
module.exports = pool;
如果我将 pool
函数包装在异步自调用函数中,我将无法导出它,因此当我需要数据库连接时可以在我的应用程序中的任何地方使用它。类似地,我的应用程序中有许多功能需要访问 secret 数据。如果我要遍历将我的所有代码包装在异步函数中的应用程序,它将继续导致更多这些困难。
问题
在我看来,最好的解决方案是异步返回数据,并在解析后同步导出。
在这种情况下如何完成这样的任务?
这里的胜利是,
- 在/secrets/index.js 中发起请求
- 在同一文件中构建 secrets 对象
- 将 secret 导出为一个对象,无需异步函数即可轻松将其导入我的应用程序中的任何其他位置。
我想如何使用它的示例
const secrets = require('../secrets');
const pool = new Pool({
user: secrets.dbUsername,
host: secrets.dbHost,
database: secrets.dbDatabase,
password: secrets.dbPassword,
port: secrets.dbPort
});
pool.on('error', err => {
console.error('Unexpected error on idle client', err);
process.exit(-1);
});
module.exports = pool;
最佳答案
因为所需的数据是异步获取的,所以没有办法让所有依赖它的东西(以某种方式)也异步。由于涉及异步性,一种可能性是通常导出可以按需调用的函数,而不是导出对象:
- 依赖于异步数据的对象在数据返回之前无法有意义地导出
- 如果您导出函数而不是对象,您可以确保控制流从您的单个入口点开始并向下游移动,而不是每个模块同时初始化自身(这在某些模块时可能会出现问题正如您所看到的,取决于其他人是否正确初始化)
另一方面,请注意,如果您有一个需要解析的 Promise
,那么调用 .then
可能比使用 async 更容易
功能。例如,而不是
const promise = require('../secrets');
(async () => {
// try/catch is needed to handle rejected promises when using await:
try {
const secrets = await promise;
// use secrets here
} catch(e) {
// handle errors
}
})();
你可能会考虑:
const promise = require('../secrets');
promise
.then((secrets) => {
// use secrets here
})
.catch((err) => {
// handle errors
});
它不那么冗长而且可能更容易一目了然 - 比自调用的 async
IIFE 更好。 IMO,使用 await
的地方是当您有多个 Promises
需要解决,并链接 .then
时s 和返回的 Promise
一起变得太丑了。
依赖于secrets
来执行的模块必须在其代码中有一些东西可以有效地等待secrets
被填充。尽管能够在您的较低代码示例中使用您的 const secrets = require('../secrets');
会不错,但那样做是不可能的.您可以导出一个将 secrets
作为参数而不是require
的函数,然后(同步!)返回
实例化的 pool
:
// note, secrets is *not* imported
function makePool(secrets) {
const pool = new Pool({
user: secrets.dbUsername,
host: secrets.dbHost,
database: secrets.dbDatabase,
password: secrets.dbPassword,
port: secrets.dbPort
});
pool.on('error', err => {
console.error('Unexpected error on idle client', err);
process.exit(-1);
});
return pool;
}
module.exports = makePool;
然后,要在另一个模块中使用它,一旦创建了secrets
,就用secrets
调用makePool
,然后使用/传递在返回的 pool
周围:
const secretsProm = require('../secrets');
const makePool = require('./makePool');
secretsProm.then((secrets) => {
const pool = makePool(secrets);
doSomethingWithPool(pool);
})
.catch((err) => {
// handle errors
});
请注意,doSomethingWithPool
函数可以完全同步,makePool
也是如此 - secret
的异步性质,一旦在 one 模块中用 .then
处理,就不必在其他任何地方异步处理,只要其他模块导出函数,而不是对象。
关于javascript - 返回异步数据然后在 Node.js 中同步导出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52827300/