Node.js Express中的异步代码

Raf*_*ael 4 asynchronous node.js express

我正在尝试使用Node.js Express创建MVC模式,但是在执行异步代码时,这似乎是一件不可能完成的任务。

举个例子:我正在从模型中的NeDB数据库中获取结果,如下所示:

控制器/database.js

// Database management stuff with NeDB
var data = {};
db.findOne({ _id: 1 }, (error, doc) => {
    if (error) console.error(error);
    data = doc;
}
Run Code Online (Sandbox Code Playgroud)

现在,我将在名为dbcontroller.js的控制器中使用它:

var nedb = require('../models/nedb.js');

module.exports.list = function(req, res, next) {
    res.render('listpage', {
        data: nedb.data,
    });
Run Code Online (Sandbox Code Playgroud)

routes / routes.js文件中:

var express = require('express');
var router = express.Router();
var dbcontroller = require('../controllers/dbcontroller.js');
// Other controllers...

router.get('/list', dbcontroller.list);
// Other route definitions...

module.exports = router;
Run Code Online (Sandbox Code Playgroud)

可爱的安排,不是吗?现在,可以肯定的是,NeDB findOne()函数是异步的,因此它将在module.exports之后执行,而不会执行。我想到的解决方案当然是将module.exports定义放入findOne()回调中!

db.findOne({ _id: 1 }, (error, doc) => {
    if (error) console.error(error);
    module.exports.data = data;
}
Run Code Online (Sandbox Code Playgroud)

我确定这在某处是反模式,但可以。还是呢?真正的戏从现在开始。

当控制器调用它时,findOne()函数也会在所有控制器逻辑之后完成,这意味着nedb.data将是未定义的。我现在必须做一些疯狂的特技...

database.js

module.exports.execute = function (callback) {
    db.findOne({ _id: 1 }, (error, doc) =>
    {
        if (error) console.error(error);
        module.exports.data = doc;
        callback();
    });
}
Run Code Online (Sandbox Code Playgroud)

dbcontroller.js

nedb.execute(export);

function export() {
    module.exports.list = function(req, res, next) {
        res.render('listpage', {
            data: nedb.data,
        });
    };
};
Run Code Online (Sandbox Code Playgroud)

而且情况变得更糟。现在,routes.js文件找不到dbcontroller.list,大概是因为它在路由请求之后仍然被定义。现在,我是否也必须开始将路由定义放入回调中?

我的观点是,整个异步性似乎完全取代了我的代码结构。以前整洁的代码现在变得很丑陋,因为我不能将代码放在它所属的位置。那只是一个异步函数,NeDB有很多异步函数,虽然很棒,但是我不知道如果findOne()已经让我头疼的话我是否可以处理它。

也许我真的没有选择,我必须弄乱我的代码以使其起作用?或者,也许我错过了一些基本的东西?也许像async或promises这样的库可以满足我的需求?你怎么看?

Mic*_*vey 5

为了使异步代码正常工作,您需要通过回调,promise或其他方式通知您何时完成异步代码。为了使上面的示例起作用,您需要做的就是在确定定义res.render 之后调用data。为此,您可以从数据库获取中返回承诺,也可以将其传递给回调函数。我建议使用promise,因为它们更容易使用,因为它们有助于避免“回调地狱”,并且Node长期以来一直对此提供支持。

控制器/database.js

module.exports.fetch = function() {
  return new Promise(function(resolve, reject) {
    db.findOne({ _id: 1 }, (error, doc) => {
      if (error) reject(error);
      resolve(doc);
   });
}
Run Code Online (Sandbox Code Playgroud)

然后,在控制器中,您只需要fetch在呈现响应之前调用(否则最终将定义数据库获取代码)。

dbcontroller.js

var nedb = require('../models/nedb.js');

module.exports.list = function(req, res, next) {
  nedb.fetch().then((data) => {
    res.render('listpage', {
        data: nedb.data,
    });
  }).catch((err) => { /* handle db fetch error */ });
}
Run Code Online (Sandbox Code Playgroud)

要记住的事情是快递不希望您立即打电话res.render。您可以执行许多异步代码,然后在完成后呈现响应。