【问题标题】:async.parallel to async await - Node.jsasync.parallel 到异步等待 - Node.js
【发布时间】:2019-01-15 20:16:20
【问题描述】:

我正在使用 express-promise-router 在来自 node.js 的查询调用中实现异步/等待。有时,当我需要为表获取数据时,我使用 async.parallel 来获取数据以及计数作为单独的数据并将它们合并。我在这里使用回调。我该如何继续使用 async/await 来处理这些?

router.post('/getDetail', (request, response, next) => {

  const id = request.body.id;
  const numPerPage = request.body.pSize;
  const pageNum = request.body.pIndex;

  let query = `select id,name,title,group_desc,unit_code from table1
              where id = '${id}'`;

  let countQuery = `select count(*) total_item from 
                   (select id,name,title,group_desc,unit_code from table1
                    where id = '${id}') a`;

  const result = {};

  async.parallel({
    rows: (cb) => {
      pool.query(
        query,
        (err, data) => {
          if (!err) {
            result.dataRows = data.rows;
            result.numPerPage = numPerPage;
            result.pageNum = pageNum;
            result.totalPage = Math.ceil(result.totalItem / numPerPage);
            result.firstItem = (pageNum - 1) * numPerPage + 1;
            result.lastItem = (pageNum * numPerPage > result.totalItem) ?
              result.totalItem : (pageNum * numPerPage)
          }
          cb(err, result)
        })
    },
    count: (cb) => pool.query(
      countQuery,
      (err, data) => {
        if (!err) {
          result.totalItem = parseInt(data.rows[0].total_item);
          result.totalPage = Math.ceil(result.totalItem / numPerPage);
          result.lastItem = (pageNum * numPerPage > result.totalItem) ?
            result.totalItem : (pageNum * numPerPage)
        }
        cb(err, result);
      })
  }, (err, result) => {
    if (err) {
      result.error = err.message;
    }
    response.json(result.rows)
  })
});

【问题讨论】:

  • 不要以async/await开头。首先了解 Promise 以及如何 Promisify 回调函数 (pool.query)
  • 另外,小心 SQL 注入!

标签: javascript node.js async-await async.js


【解决方案1】:

我试图使代码与您上面的代码相似,但最后我注意到您使用更高级别的 result 对象来传递引用。

在下面的代码中,我没有使用async/await,因为对于这个例子来说这不是完全必要的,因为pool.query 方法需要一个回调。相反,我创建了返回 Promises 的 rowscount 函数。然后我将它们与Promise.all 一起使用,这将使两个promise 并行运行,并以与promise 传递给Promise.all 相同的顺序将结果作为数组返回。当pool.query 返回一个promise 时,请参阅下面的示例使用async/await

router.post('/getDetail', (request, response, next) => {

  const id = request.body.id;
  const numPerPage = request.body.pSize;
  const pageNum = request.body.pIndex;

  let query = `select id,name,title,group_desc,unit_code from table1
              where id = '${id}'`;

  let countQuery = `select count(*) total_item from 
                   (select id,name,title,group_desc,unit_code from table1
                    where id = '${id}') a`;

  const result = {};
  Promise.all([
    rows(),
    count()
  ])
  .then(results => {
    // results is an array and the first item is `rows`
    response.json(results[0]);
  })
  .catch(err => {
    // handle error
  });

  function rows() {
    return new Promise((resolve, reject) => {
      pool.query(
        query,
        (err, data) => {
          if (err) {
            return reject(err);
          }

          result.dataRows = data.rows;
          result.numPerPage = numPerPage;
          result.pageNum = pageNum;
          result.totalPage = Math.ceil(result.totalItem / numPerPage);
          result.firstItem = (pageNum - 1) * numPerPage + 1;
          result.lastItem = (pageNum * numPerPage > result.totalItem) ?
            result.totalItem : (pageNum * numPerPage);
          resolve(result);
        });
    });
  }

  function count() {
    return new Promise((resolve, reject) => {
      pool.query(
        countQuery,
        (err, data) => {
          if (err) {
            return reject(err);
          }
          result.totalItem = parseInt(data.rows[0].total_item);
          result.totalPage = Math.ceil(result.totalItem / numPerPage);
          result.lastItem = (pageNum * numPerPage > result.totalItem) ?
            result.totalItem : (pageNum * numPerPage);
          resolve(result);
        });
    });
  }
});

下一个例子展示了使用async/await 假设pool.query 可以返回一个Promise。我还假设对象引用仍将被维护,这就是为什么我在try/catchtry 部分分配result.rows = results[0]

router.post('/getDetail', async(request, response, next) => {

  const id = request.body.id;
  const numPerPage = request.body.pSize;
  const pageNum = request.body.pIndex;

  let query = `select id,name,title,group_desc,unit_code from table1
              where id = '${id}'`;

  let countQuery = `select count(*) total_item from 
                   (select id,name,title,group_desc,unit_code from table1
                    where id = '${id}') a`;

  const result = {};
  try {
    const results = await Promise.all([rows(), count()]);
    // results is an array and the first item is `rows`
    result.rows = results[0];
  } catch (err) {
    result.error = err.message;
  }
  response.json(result.rows);

  async function rows() {
    const data = await pool.query(query);
    result.dataRows = data.rows;
    result.numPerPage = numPerPage;
    result.pageNum = pageNum;
    result.totalPage = Math.ceil(result.totalItem / numPerPage);
    result.firstItem = (pageNum - 1) * numPerPage + 1;
    result.lastItem = (pageNum * numPerPage > result.totalItem)
      ? result.totalItem
      : (pageNum * numPerPage);
    return result;
  }

  async function count() {
    const data = await pool.query(countQuery);
    result.totalItem = parseInt(data.rows[0].total_item);
    result.totalPage = Math.ceil(result.totalItem / numPerPage);
    result.lastItem = (pageNum * numPerPage > result.totalItem)
      ? result.totalItem
      : (pageNum * numPerPage);
    return result;
  }
});

我希望这可以帮助您了解如何使用 async/await 进行并行处理。我看到的唯一可能出现在您的代码中的问题是,如果 rows 查询在 count 查询之前返回,因为 totalItem 尚未设置。

编辑:就个人而言,我想我会用 1 个查询而不是 2 个不同的查询来处理这样的逻辑:

router.post('/getDetail', async(request, response, next) => {
  const id = request.body.id;
  const numPerPage = request.body.pSize;
  const pageNum = request.body.pIndex;

  let query = `select id,name,title,group_desc,unit_code from table1
              where id = '${id}'`;

  const result = {};
  try {
    const data = await pool.query(query);
    result.dataRows = data.rows;
    result.totalItem = data.rows.length; // data might already have a property for this
    result.numPerPage = numPerPage;
    result.pageNum = pageNum;
    result.totalPage = Math.ceil(result.totalItem / numPerPage);
    result.firstItem = (pageNum - 1) * numPerPage + 1;
    result.lastItem = (pageNum * numPerPage > result.totalItem)
      ? result.totalItem
      : (pageNum * numPerPage);
  } catch (err) {
    result.error = err.message;
  }

  response.json(result);
});

编辑 2:在 cmets 中进行了更多讨论后,我认为这个版本会更好。它不使用主 result 对象来提供引用并将基于“计数”的属性移动到 count 函数以允许并行执行:

router.post('/getDetail', async(request, response, next) => {

  const id = request.body.id;
  const numPerPage = request.body.pSize;
  const pageNum = request.body.pIndex;

  let query = `select id,name,title,group_desc,unit_code from table1
              where id = '${id}'`;

  let countQuery = `select count(*) total_item from 
                   (select id,name,title,group_desc,unit_code from table1
                    where id = '${id}') a`;

  let result = { numPerPage, pageNum };
  try {
    const results = await Promise.all([rows(), count()]);
    // this will combine the properties from both results onto the `result` object
    result = Object.assign({}, result, ...results);
  } catch (err) {
    result.error = err.message;
  }
  response.json(result);

  async function rows() {
    const data = await pool.query(query);
    return { dataRows: data.rows };
  }

  async function count() {
    const data = await pool.query(countQuery);
    // redefining result here to be only used in this scope
    const result = {};
    result.totalItem = parseInt(data.rows[0].total_item);
    result.totalPage = Math.ceil(result.totalItem / numPerPage);
    result.firstItem = (pageNum - 1) * numPerPage + 1;
    result.lastItem = (pageNum * numPerPage > result.totalItem)
      ? result.totalItem
      : (pageNum * numPerPage);
    return result;
  }
});

【讨论】:

  • 谢谢@doowb。我尝试了异步方法。我做了一个 console.log(result.rows) 并得到了数据。但在 UI 中,我得到的只是一个空对象。
  • 您能指导我解决异步/等待问题吗?
  • 如果您的数据恢复正常,您可能不会遇到问题。我很难说,但我给出的答案是你将如何使用async/await 和一起承诺而不是回调。 (顺便说一句......我之前没有看到你的 cmets,因为我直到现在才收到 SO 的通知)
  • 是的,数据恢复正常。我能够记录它并查看数据。但在 UI 中,我只能看到一个空对象。正如您所说,行查询可能会在计数查询之前返回,这可能就是问题所在。
  • 我添加了第三个代码块,说明我将如何亲自处理该查询。它现在可能会显示您的数据。
猜你喜欢
  • 2021-03-26
  • 1970-01-01
  • 2019-04-22
  • 2021-04-25
  • 2014-04-28
  • 2019-01-13
  • 2020-08-01
  • 1970-01-01
  • 2021-08-07
相关资源
最近更新 更多