我正在尝试使用 Node.js Express 创建 MVC 模式,但在执行异步代码时这似乎是一项完全不可能完成的任务。
恰当的例子:我正在从模型中的 NeDB 数据库中获取结果,如下所示:
controllers/database.js
// Database management stuff with NeDB
var data = {};
db.findOne({ _id: 1 }, (error, doc) => {
if (error) console.error(error);
data = doc;
}
现在,我将在名为 dbcontroller.js 的 Controller 中使用它:
var nedb = require('../models/nedb.js');
module.exports.list = function(req, res, next) {
res.render('listpage', {
data: nedb.data,
});
在 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;
可爱的安排,不是吗? 现在,可以肯定的是,NeDB findOne() 函数是异步的,所以它将在 module.exports 之后执行,但这是行不通的。想到的解决方案当然是将 module.exports 定义放在 findOne() 回调中!
db.findOne({ _id: 1 }, (error, doc) => {
if (error) console.error(error);
module.exports.data = data;
}
我确定这是某个地方的反模式,但它确实有效。或者是吗?真正的戏剧现在开始。
当它被 Controller 调用时,findOne() 函数也会在所有 Controller 逻辑之后完成,这意味着 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();
});
}
dbcontroller.js:
nedb.execute(export);
function export() {
module.exports.list = function(req, res, next) {
res.render('listpage', {
data: nedb.data,
});
};
};
而且情况变得更糟。routes.js 文件现在找不到 dbcontroller.list,大概是因为它仍然定义在 之后路线要求它。现在,我是否也必须开始将路由定义放入回调中?
我的观点是,整个异步性似乎完全占据了我的代码结构。以前整洁的代码现在变得一团糟,因为我无法将代码放在它所属的位置。这只是 一个 异步函数,NeDB 有很多异步函数,虽然这很好,但如果 findOne() 已经让我头疼了,我不知道我是否能处理它。
也许我真的别无选择,我必须弄乱我的代码才能让它工作?或者也许我错过了一些非常基本的东西?或者像 async 或 promises 这样的库可以满足我的需求?你怎么看?
最佳答案
为了让异步代码工作,您需要通过回调、 promise 或其他方式通知您何时完成。要使上面的示例正常工作,您需要做的就是调用 res.render
after 您确定 data
已定义。为此,您可以从数据库提取中返回一个 promise ,或将其传递给回调。我会建议 promise ,因为它们更容易使用,因为它们有助于避免 "callback hell" ,并且 Node 长期以来一直对它们提供原生支持。
controllers/database.js
module.exports.fetch = function() {
return new Promise(function(resolve, reject) {
db.findOne({ _id: 1 }, (error, doc) => {
if (error) reject(error);
resolve(doc);
});
}
然后,在您的 Controller 中,您只需在呈现响应之前调用 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 */ });
}
要记住的是,express 并不期望您立即调用 res.render
。您可以执行大量异步代码,然后在完成后呈现响应。
关于node.js - Node.js Express 中的异步代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39708902/