返回异步数据,然后在Node.js中同步导出 [英] Returning asynchronous data then exporting it synchronously in Node.js

查看:57
本文介绍了返回异步数据,然后在Node.js中同步导出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我正在从AWS Secrets Manager返回数据,并使用 aws-sdk 这样做.早些时候我问了一个有关如何正确返回数据并导出数据的问题,因为在导出导出到其他地方之前,导出的对象从未解析过数据.这使我得到了一堆不确定的信息.

I am returning data from AWS Secrets Manager and using the aws-sdk to do so. Earlier I asked a question about how to correctly return the data and export it since the exported object never had the data resolved by the time the export was imported somewhere else. This caused me to get a bunch of undefined.

解决该问题后,确定处理该问题的方法是将aws-sdk函数包装在一个promise中,然后使用async等待在另一个文件中调用promise.这给我带来了问题.

After solving that problem it was determined that the way to handle this was to wrap the aws-sdk function in a promise, then call the promise in another file with async await. This causes me issues.

示例

如果我这样请求并从AWS返回数据,

If I request and return the data from AWS like this,

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;

然后,我可以将其导入另一个文件并使用这样的数据,

Then I can import it in another file and use the data like this,

const promise = require('../secrets');

(async () => {
  const secrets = await promise;
  // use secrets here
})();

现在让我们在该文件中尝试使用机密的地方,我有这样的内容,

Now let's say in that file where I am trying to use secrets I have something like this,

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 函数包装在异步自调用函数中,则导出时会遇到麻烦,因此当我需要数据库连接时,可以在我的应用程序中的任何地方使用它.类似地,我在整个应用程序中都有许多需要访问机密数据的功能.如果我要遍历应用程序,将所有代码包装在异步函数中,它将继续引起更多这些困难.

If I wrap the pool function in the async self invoking function I have trouble exporting it so it can be used anywhere in my app when I need a database connection. Similar, I have many functions throughout my application that need access to the secret data. If I were to walk through the application wrapping all of my code in async functions it would continue to cause more of these difficulties.

问题

在我看来,最好的解决方案是异步返回数据,并在数据解析后同步导出.

It seems to me the best solution here would be to return the data asynchronously and once it has resolved, export it synchronously.

在这种情况下我如何完成这样的任务?

How can I achieve such a task in this scenario?

这里将是一个胜利,

  1. 在/secrets/index.js中进行请求
  2. 在同一文件中构建机密对象
  3. 将机密作为对象导出,可以轻松将其导入到应用程序中的其他任何位置,而无需异步功能.

我如何使用此示例

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;

推荐答案

由于所需的数据是异步获取的,因此也无法使依赖于它的所有内容(某种程度上)也是异步的.考虑到异步性,一种可能性是通常导出可以按需调用的功能,而不是导出对象:

Because the needed data is gotten asynchronously, there's no way around making everything that depends on it (somehow) asynchronous as well. With asynchronicity involved, one possibility is to usually export functions that can be called on demand, rather than exporting objects:

  • 依赖异步数据的对象在数据返回之前无法被有意义地导出
  • 如果导出函数而不是对象,则可以确保控制流从您的单个入口点开始并向下游移动,而不是每个模块都一次初始化自身(这在某些模块出现问题时可能会出现问题)如您所见,依赖其他人正确初始化)
  • an object that depends on the asynchronous data can't be meaningfully exported before the data comes back
  • if you export functions rather than objects, you can ensure that control flow starts from your single entry point and heads downstream, rather than every module initializing itself at once (which can be problematic when some modules depend on others to be initialized properly, as you're seeing)

另外,请注意,如果您有一个需要解决的 Promise ,则在其上调用 .then 可能比使用 async更容易函数.例如,而不是

On another note, note that if you have a single Promise that needs to resolve, it's probably easier to call .then on it than use an async function. For example, rather than

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
  });

它不那么罗and,而且乍一看可能更容易理解-比自我调用的 async IIFE更好.IMO,使用 await 的地方是当您有多个 Promises 需要解决并链接 .then 时s和返回的 Promise s太难看了.

It's less wordy and probably easier to make sense of at a glance - better than a self-invoking async IIFE. IMO, the place to use await is when you have multiple Promises that need to resolve, and chaining .thens and returned Promises together gets too ugly.

依赖于 secrets 来执行 has 的模块,在其代码中具有有效地等待 secrets 填充的内容.尽管在下面的代码示例中可以使用您的 const secrets = require('../secrets'); nice ,但这是不可能的.您可以导出将 secrets 作为 parameter 而不是 require 的函数,然后(同步!) return 实例化的 pool :

A module that depends on secrets to perform has to, in its code, have something that effectively waits for secrets to be populated. Although being able to use your const secrets = require('../secrets'); in your lower code example would be nice, it just isn't possible like that. You can export a function that takes secrets as a parameter rather than as a require, and then (synchronously!) return the instantiated 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 ,然后使用/pass围绕返回的 pool :

Then, to use it in another module, once the secrets are created, call makePool with the secrets, and then use / pass around the returned pool:

const secretsProm = require('../secrets');
const makePool = require('./makePool');
secretsProm.then((secrets) => {
  const pool = makePool(secrets);
  doSomethingWithPool(pool);
})
.catch((err) => {
  // handle errors
});

请注意, doSomethingWithPool 函数可以是完全同步,而 makePool -是 secrets 的异步特性.,一旦在 one 模块中用 .then 处理过,就不必在其他任何地方进行异步处理,只要其他模块导出 functions ,而不是对象.

Note that the doSomethingWithPool function can be completely synchronous, as is makePool - the asynchronous nature of secrets, once handled with .then in one module, does not have to be dealt with asynchronously anywhere else, as long as other modules export functions, rather than objects.

这篇关于返回异步数据,然后在Node.js中同步导出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆