我对异步编程还有点新意,我一直想知道是否有更好的方法来完成我想要做的事情。
exports.content = function(req, res){
OPID = req.params.id;
titles.findOne({postID: OPID}, function (err, post) { //function #1
if (err) throw(err);
readComment(OPID, function(comment){ //function #2
branchFilter.getBranches(function(branches){ //function #3
res.render('content', {title: post.title, content: post.body, OPID: post.postID, comments: comment, branches: branches});
})
});
});
};
在这个例子中,我有三个嵌套函数,它使用回调来从其他模块中检索数据。我需要在res.render语句中包含所有这些数据。我想如果我继续这种方法,我将需要更多的嵌套函数。有没有更好的方法呢?
答案 0 :(得分:2)
例如,有一个只获取数据的函数
exports.content = function(req, res){
getData(req, function(err, data) {
// trivial error handling
if (err) {
console.dir('error getting your data', err);
return res.redirect('/');
}
res.render('content', data)
}
// cb is a callback function
function getData(req, cb) {
OPID = req.params.id;
titles.findOne({postID: OPID}, function (err, post) { //function #1
if (err) { return cb(err); }
readComment(OPID, function(err, comment){ //function #2 (with error handling)
if (err) { return cb(err); }
branchFilter.getBranches(function(err, branches){ //function #3
if (err) { return cb(err); }
var output = {
title: post.title,
content: post.body,
OPID: post.postID,
comments: comment, branches: branches
}
return cb(null, output);
});
});
});
}
看看http://callbackhell.com/给出了一个非常好的概述,即如何使用写清洁代码 回调。从该站点复制以下内容
这是一些(杂乱的)浏览器javascript,它使用浏览器请求向服务器发出AJAX请求:
var form = document.querySelector('form')
form.onsubmit = function(submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, function(err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
})
}
此代码有两个匿名函数。让我们给出名字!
var form = document.querySelector('form')
form.onsubmit = function formSubmit(submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, function postResponse(err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
})
}
正如您所看到的,命名函数非常简单,并为您的代码做了一些不错的事情:
在最后一个例子的基础上,让我们更进一步,摆脱代码中正在进行的三级嵌套:
function formSubmit(submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse(err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
document.querySelector('form').onsubmit = formSubmit
这样的代码看起来不那么可怕,以后更容易编辑,重构和破解。
这是最重要的部分:任何人都能够创建模块(AKA库)。引用Isaac Schlueter(node.js项目):"编写每个做一件事的小模块,并将它们组装成其他更重要的模块。如果你不去那里,你就无法进入回调地狱。"
让我们从上面取出样板代码,然后通过将其分成几个文件将其转换为模块。由于我在浏览器和服务器上编写JavaScript,我将展示一种既适用又兼顾的方法,但仍然很简单。
这是一个名为formuploader.js的新文件,它包含我们之前的两个函数:
function formSubmit(submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse(err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
exports.submit = formSubmit
答案 1 :(得分:1)
正如JohnnyHK建议的那样,一个好的异步库可以帮助你解决这个问题。我还建议Caolan's async。您可以使用auto
和forEach
的组合解决几乎所有异步问题,但如果运行时出现问题async
可能不是最佳选择。我会按如下方式重写您的代码:
exports.content = function(req, res){
OPID = req.params.id;
async.auto({
post: function(next) {
titles.findOne({postID: OPID}, next);
},
comment: function(next) {
readComment(OPID, function(comment){ next(null, comment); });
},
branches: function(next) {
branchFilter.getBranches(function(branches){ next(null, branches); });
}
}, function(err, results) {
if(err) throw(err);
res.render('content', {title: results.post.title, content: results.post.body, OPID: results.post.postID, comments: results.comment, branches: results.branches});
});
};
auto
所做的是获取函数字典(可选依赖于字典中的其他函数 - 请参阅异步自述文件以获取更多信息)并将结果作为字典传递给第二个参数,即“最终”功能
如果任何函数将非空err
传递给其回调(第一个参数),它将停止调用其他函数并立即将err
和null
结果传递给“最终”功能。
请注意我必须在branches
和comment
函数中传递匿名函数。这是因为auto
与此async
库中的其他函数一样,期望其回调的第一个参数是错误值,第二个参数是结果。