NodeJS Express 异步/等待

mzi*_*ert 5 javascript node.js express async-await

仍然掌握了 Node 的非阻塞性质。以下代码按预期执行。但是,我想知道是否有更好的方法来完成任务。

有 3 个参数提供给路由(邮政编码、类型、rad)。从那里我使用 NPM Zipcode 包在提供的 rad 内返回一组邮政编码。

然后我在异步函数中的 zips 数组上使用 for 循环,并等待执行 MySQL 查询并返回承诺的函数的响应。然后返回一组用户对象。

我不确定的是我是否正确发送了响应,或者是否有更有效的方法来编写此代码。

谢谢。

router.get('/:zipcode/:type/:rad', function (req, res) {

  const rad = req.params.rad;
  const zip = req.params.zipcode;
  let zips = zipcodes.radius(zip, rad);
  zips.push(zip);

  let type;
  if(req.params.type === 'bartenders') {
    type = 0;
  } else {
    type = 1;
  }

  const params = {
    'type': type,
    'zips': zips
  };


  function userGroup(type, zip) {
    return new Promise(resolve => {
      connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
        if(err) throw err;
        resolve(result);
      });
    });
  }


  async function getUsers(params) {
    let userList = [];
    for (i = 0; i < params.zips.length; i++) {
      const users = await userGroup(params.type, params.zips[i]);
      for (u = 0; u < users.length; u++) {
        userList.push(users[u]);
      }
    }
    return userList;
  }


  function sendUsers(callback) {
    getUsers(params).then( res => {
      callback(null, res)
    })
  }


  sendUsers(function(err, result) {
    if(err) throw err;
    res.send(result)
  })


});
Run Code Online (Sandbox Code Playgroud)

Cir*_*四事件 7

Express 5 将自动正确处理异步错误

https://expressjs.com/en/guide/error-handling.html目前说得很清楚:

从 Express 5 开始,返回 Promise 的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value)。例如:

app.get('/user/:id', async function (req, res, next) {
 var user = await getUserById(req.params.id)
 res.send(user)
})
Run Code Online (Sandbox Code Playgroud)

如果 getUserById 抛出错误或拒绝,则将使用抛出的错误或拒绝的值调用 next 。如果未提供拒绝值,则将使用 Express 路由器提供的默认错误对象来调用 next。

我已经在实验中证明了这一点:将异步函数传递给 Node.js Express.js 路由器

这意味着您将能够直接进行回调async并使用await它,而无需任何额外的包装器:

router.get('/:zipcode/:type/:rad', async (req) => {
  ...
  return await getUsers({ type, zips });
});
Run Code Online (Sandbox Code Playgroud)

请注意,截至 2021 年 12 月,Express 5 仍处于 alpha 版本,不建议用于生产使用。


Tim*_*ong 5

与手动将每个回调函数转换为 Promise 不同,创建一个包装器来支持 async/await 会更容易。

参考
https://strongloop.com/strongblog/async-error-handling-expressjs-es7-promises-generators/

function asyncWrapper(fn) {
  return (req, res, next) => {
    return Promise.resolve(fn(req))
      .then((result) => res.send(result))
      .catch((err) => next(err))
  }
}
Run Code Online (Sandbox Code Playgroud)

示例代码

async createUser(req) {
  const user = await User.save(req.body)
  return user
}

router.post('/users', asyncWrapper(createUser))
Run Code Online (Sandbox Code Playgroud)

重构你的代码

function userGroup(type, zip) {
  return new Promise(resolve => {
    connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
      if(err) throw err;
      resolve(result);
    });
  });
}

async function getUsers({ type, zips }) {
  let userList = [];
  // [IMPORTANT]
  // - Replaced the for-loop to for-of-loop for await to work.
  // - This is not efficient because the `userGroup` function is run one by one serially.
  for (let zip of zips) {
    const users = await userGroup(type, zip);
    userList = userList.concat(users);
  }
  return userList;
}

router.get('/:zipcode/:type/:rad', asyncWrapper(async (req) => {
  const rad = req.params.rad;
  const zip = req.params.zipcode;
  let zips = zipcodes.radius(zip, rad);
  zips.push(zip);

  let type;
  if(req.params.type === 'bartenders') {
    type = 0;
  } else {
    type = 1;
  }

  return await getUsers({ type, zips });
}));
Run Code Online (Sandbox Code Playgroud)

为了进一步提高效率,就应该更换换的环内getUsersPromise.map所提供的蓝鸟Promise.map将并行运行承诺。

async function getUsers({ type, zips }) {
  let userList = []
  const userListList = await Promise.map(zips, (zip) => {
    return userGroup(type, zip);
  });
  for (let users of userListList) {
    userList = userList.concat(users)
  }
  return userList;
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*gin 3

当您不在异步函数内时,不应抛出错误。

function userGroup(type, zip) {
    return new Promise( (resolve,reject) => {
      connection.query(`SELECT * FROM bt9.users WHERE zip = ${zip} AND type = ${type} AND display = 1`, function (err, result) {
        if(err) return reject(err); //<- reject and return
        resolve(result);
      });
    });
  }
Run Code Online (Sandbox Code Playgroud)

此外,您可以使用Promise.allPromise 数组,而不是await在每个循环迭代中使用。这将允许并行执行您的连接。