所以我有一个帖子集合
{
id: String,
comments: [String], # id of Comments
links: [String], #id of Links
}
评论: { id:String, 评论:字符串, }
链接: { id:String, 链接:字符串, }
通过ID:
查找包含评论和链接的帖子Posts.findOne({id: id}, function(post) {
Comments.find({id: post.id}, function(comments) {
Links.find({id: post.id}, function(links) {
res.json({post: post, comments: comment, links: links})
})
})
})
如何使用Promise(http://mongoosejs.com/docs/promises.html)来避免回调地狱?
var query = Posts.findOne({id: id});
var promise = query.exec();
promise.then(function (post) {
var query1 = Comments.find({id: post.id});
var promise1 = query1.exec();
promise1.then(function(comments) {
var query2 = Links.find({id: post.id});
var promise2 = query2.exec();
promise2.then(function(links) {
res.json({post: post, comments: comment, links: links})
})
})
});
似乎没有好处......
答案 0 :(得分:4)
尝试使用此功能:
function getPost(id) {
return Post
.findOne({id: id})
.then( post => {
return post;
});
}
使用Q模块
function getCommentsAndLinks(post) {
return Q.all([
Comment.find({id: post.id}),
Links.find({id: post.id})
])
.done( results => {
let comments = results[0];
let links = results[1];
return [post, comments, links];
})
.catch( err => {
// handle err
})
控制器上的
getPost(postId)
.then(getCommentsAndLinks)
.then( results => {
let post = results[0];
let comments = results[1];
let links = results[2];
// more code here
})
.catch( err => {
// handle err
})
但我建议您不要保存IDS字符串,保存对象的实例,这样您就可以使用populate来获取评论和链接的所有数据,如下所示:
Post
.findOne({id: id})
.populate('comments')
.populate('links')
.then( post => {
// here have the post with data of comments and links
});
答案 1 :(得分:2)
您正在嵌套回调。你不需要这样做。如果您从.then
返回承诺,则当 承诺得到解决时,您链接到的任何.then
都将得到解决:
promise.then(post => Comments.find({id: post.id})
.then(comments => Links.find({id: post.id})
.then(links => {});
评论查询不依赖于链接,因此您实际上可以同时执行两个查询:
promise.then(post => {
return Promise.all([
post,
Comments.find({id: post.id}),
Links.find({id: post.id}),
]);
}).then(data => res.json({
post: data[0],
comments: data[1],
links: data[2],
});
如果您使用像bluebird这样的库,您还可以使用类似spread
运算符的内容来使名称更加透明。
我还会考虑将co用于基于生成器的控制流,因为我认为这更加清晰:
co(function* () {
const post = yield Posts.findOne({id});
const [comments, links] = yield [
Comments.find({id: post.id}),
Links.find({id: post.id}),
];
res.json({post, comments, links});
});
答案 2 :(得分:1)
你可以使用这样的承诺来做到这一点:
Posts.findOne({id: id}).exec().then(function(post) {
let p1 = Comments.find({id: post.id}).exec();
let p2 = Links.find({id: post.id}).exec();
return Promise.all([p1, p2]).then(function(results) {
res.json({post: post, comments: results[0], links: results[1]});
});
}).catch(function(err) {
// error here
});
这设置了两个操作Comments.find().exec()
和Links.find().exec()
,它们都依赖于post
变量,但彼此独立,因此它们可以并行运行。然后,它使用Promise.all()
知道两者何时完成,然后输出JSON。
以下是逐步说明:
Posts.findOne().exec()
。Comments.find().exec()
和Links.find().exec()
。Promise.all()
了解两者何时完成。这可以通过较少的嵌套来完成,但由于您在后续请求或最终JSON中使用先前结果,因此更容易嵌套它。
在另一个答案How to chain and share prior results中链接承诺请求时,您可以看到分享先前结果的各种选项。
仅供参考,与你在问题中显示的内容相比,这个承诺实现真的很好用于错误处理。您的非承诺代码没有显示错误处理,但promise版本会将所有错误传播到.catch()
处理程序。
答案 3 :(得分:1)
使用promises的优点是可以链接它们,因此您的代码可以简化为:
let post, comments;
Posts.findOne({id: id}).exec().then(_post => {
post = _post;
return Comments.find({id: post.id}).exec();
}).then(_comments => {
comments = _comments;
return Links.find({id: post.id}).exec();
}).then(links => res.json({post, comment, links}))
.catch(error => res.error(error.message));
你会注意到我只需要一个捕获块。
答案 4 :(得分:1)
这是一个更短的版本
Posts.findOne({id: id}).then(function (post) {
var query1 = Comments.find({id: post.id});
var query2 = Links.find({id: post.id});
Promise.all(query1.exec(), query2.exec()).then(function(data) {
res.json({ post: post, comments: data[0], links: data[1] });
});
});
答案 5 :(得分:-3)
在我看来,你不能避免回调地狱。这是异步编程的本质。你应该利用异步编程,而不是试图让它看起来像同步。
你必须使用回调来创建一个承诺,只是为了实现"然后"句法。 "然后"语法看起来更好,但没有真正提供任何有用的回调,为什么这么麻烦。承诺唯一有用的功能是$('div').html('one').delay(5000).html('two');
,您可以用它来等待所有承诺的完成。
尝试使用rxjs来处理异步问题。您仍然必须使用回调来创建rxjs observable。但rxjs提供了许多功能来帮助您利用异步编程,而不是避免它。