async.parallel to async await - Node.js

烂漫一生 提交于 2019-12-11 15:56:57

问题


I am using express-promise-router to achieve async/await in my query calls from node.js. Sometimes, when I need to fetch data for tables, I use async.parallel to get the data as well as the count as separate data and merge them. I am using callbacks here. How do I go ahead using async/await for these?

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

回答1:


I tried to keep the code similar to what you have above, but I noticed at the end that you're using a higher level result object to pass references around.

In the below code, I'm not using async/await since that's not entirely necessary for this example since the pool.query method takes a callback. Instead, I created rows and count functions that return Promises. Then I use those with Promise.all which will let both promises run in parallel and return the results as an array in the same order the promises are passed to Promise.all. See further down for an example using async/await when pool.query returns a promise instead.

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

The next example shows using async/await assuming pool.query can return a Promise. I'm also making the assumption that the object references will still be maintained and is why I'm assigning result.rows = results[0] in the try part of the try/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;
  }
});

I hope this helps you understand how you can do parallel processing with async/await. The only issue I see that may come up in your code is if the rows query returns before the count query since totalItem won't be set yet.

EDIT: personally, I think I would handle the logic like this with 1 query instead of 2 different queries:

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

EDIT 2: After more discussion in the comments, I think this version will work better. It doesn't use the main result object to provide references and moves the "count" based properties to the count function to allow for parallel execution:

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


来源:https://stackoverflow.com/questions/51748818/async-parallel-to-async-await-node-js

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!