在Nodejs + Mongoose中异步/等待

时间:2019-10-01 17:03:37

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

我是Promises和async / await编程的新手,我不确定自己是否会直接理解它。我正在使用Express,Mongoose和MongoDB在Node.js中创建API。 我已经看过很多关于如何处理异步性的教程,但是所有这些教程都是关于NodeJs项目的,这些项目中的路由和数据库查询位于同一文件中。 例如:

const asyncMiddleware = fn =>
  (req, res, next) => {
    Promise.resolve(fn(req, res, next))
      .catch(next);
};

router.get('/users/:id', asyncMiddleware(async (req, res, next) => {
    const something = await getSomethingFromDb({ id: req.params.id })
    res.json(something);
}));

但是,为清楚起见,我已将路由与控制器分开,但我怀疑是否正确完成了路由。这是我的代码:

router.js

const asyncMiddleware = fn =>
  (req, res, next) => {
    Promise.resolve(fn(req, res, next))
      .catch(next);
};

router.get('/something/:id', asyncMiddleware(async (req, res, next) => {
    const answer = await somethingController.findById(req, res, next)
}));

controller.js

exports.findById = async (req, res, next) => {
    const something = await Something.findById(req.params.id).exec();
    res.send(something);
};

我试图用console.log()的东西来检查打印了什么,但是我意识到,由于等待的部分,这整段代码将等待查询完成。 这执行得好吗?我该如何测试?

版本: NodeJs v10.16.3 猫鼬v5.7.1

2 个答案:

答案 0 :(得分:5)

首先,您不需要“ asyncMiddleware”。让我举一个完整的示例,说明如何在保持控制器异步的同时分隔路由和控制器:

控制器

exports.findById = async (req, res, next) => {
    try{
       const something = await Something.findById(req.params.id).exec();
       res.send(something);
    }catch(err){
       return res.status(500).send({
        message: err.message
      })
    }   
};

您应该将async调用包装在try / catch块中。

路线

然后您可以像这样简单地在您的路由中呼叫您的控制器:

router.get('/:id', Controller.findByID)

就是这样。您不需要在路由上进行任何额外的async呼叫。

如果您有要添加路由的中间件,则可以这样做:

//for single middleware
router.get('/:id',somethingMiddle,Controller.findByID)

//for multiple middleware
router.get('/:id',[somethingMiddle, anotherMiddle],Controller.findByID)

让我知道这是否有帮助

答案 1 :(得分:1)

就个人而言,我会坚持将try-catch用于异步功能,而不是使用中间件。这可能看起来像以下内容:

module.exports.view = async function(req, res, next) {
  try {
    var something = await Something.findById(req.params.id).orFail();
    res.send(something);
  } catch (err) {
    next(err);
  }
};

那么在您的路由器中,它就是:

router.get('/something/:id', somethingController.view);

通过这种方式,您可以在next(err)将其发送给全局错误处理程序之前,为该控制器操作处理和处理特殊的错误情况。

现在,我不确定这是否是您所期望的,但是您可能想在查询中致电.orFail()。这样,如果未找到具有指定ID的“内容”,则会引发错误。否则something变量将为null。但这完全取决于您希望API的运行方式。

此外,在定义async函数时,基本上是说该函数返回一个Promiseawait语句基本上告诉程序暂停功能,直到承诺被解决为止。在此示例中,await将等待数据库返回结果,然后再继续执行控制器操作。 Express会神奇地处理等待您的async路由方法和中间件的情况。