首页 > 解决方案 > async.parallel 到异步等待 - Node.js

问题描述

我正在使用 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)
  })
});

标签: javascriptnode.jsasync-awaitasync.js

解决方案


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

在下面的代码中,我没有使用async/await,因为对于这个例子来说这不是完全必要的,因为该pool.query方法需要一个回调。相反,我创建了返回 Promisesrowscount函数。然后我使用那些Promise.all可以让两个 Promise 并行运行并将结果作为数组以与 Promise 传递给的顺序相同的顺序返回Promise.all。请参阅下面的示例,以查看使用async/awaitwhenpool.query返回承诺的示例。

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。我还假设对象引用仍将被维护,这就是我result.rows = results[0]在.trytry/catch

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:在评论中进行了更多讨论后,我认为这个版本会更好。它不使用主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;
  }
});

推荐阅读