So I have a Posts collection
{
id: String,
comments: [String], # id of Comments
links: [String], #id of Links
}
Comments: { id: String, comment: String, }
Links: { id: String, link: String, }
Find a post with comments and links belong to it by 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})
})
})
})
How to use Promise(http://mongoosejs.com/docs/promises.html) to avoid callback hell?
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})
})
})
});
Seems no good......
We can avoid the callback hell with the help of Promises. Promises in javascript are a way to handle asynchronous operations in Node. js. It allows us to return a value from an asynchronous function like synchronous functions.
To convert a callback into a promise, you need to return a promise. You run the code with the callback inside the promise. const readFilePromise = () => { return new Promise((resolve, reject) => { fs. readFile(filePath, options, (err, data) => { // ... }) }) }
They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events. In other words also, we may say that, promises are the ideal choice for handling multiple callbacks at the same time, thus avoiding the undesired callback hell situation.
This is a big issue caused by coding with complex nested callbacks. Here, each and every callback takes an argument that is a result of the previous callbacks. In this manner, The code structure looks like a pyramid, making it difficult to read and maintain.
You are nesting the callbacks. You don't need to do this. If you return a promise from .then
then any .then
you chain to it will be resolved when that promise gets resolved:
promise.then(post => Comments.find({id: post.id})
.then(comments => Links.find({id: post.id})
.then(links => {});
The comments query does not depend on links so you can actually do both queries at once:
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],
});
If you use a library like bluebird you can also use something like the spread
operator to make the names more transparent.
I would also look into using co for generator-based control flow as I think this is even clearer:
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});
});
Try whit this:
function getPost(id) {
return Post
.findOne({id: id})
.then( post => {
return post;
});
}
using Q module
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
})
on controller
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
})
but i suggest you not save string of IDS, save the instance of object, so you can use populate to get all data of comments and links, something like this:
Post
.findOne({id: id})
.populate('comments')
.populate('links')
.then( post => {
// here have the post with data of comments and links
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With