How to use promise to avoid callback hell? [duplicate] How to use promise to avoid callback hell? [duplicate] mongoose mongoose

How to use promise to avoid callback hell? [duplicate]


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});


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});});


You can do it using promises like this:

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});

This sets up two operations Comments.find().exec() and Links.find().exec() that both depend upon the post variable, but are independent of each other so they can run in parallel. Then, it uses Promise.all() to know when both are done and then the JSON can be output.

Here's the step by step description:

  1. Run Posts.findOne().exec().
  2. When it's done, start both Comments.find().exec() and Links.find().exec() in parallel.
  3. Use Promise.all() to know when both of those are done.
  4. When both of those are done, then output the JSON.

This could be done with less nesting, but because you're using prior results in subsequent requests or in the final JSON, it's easier to nest it a bit.

You can see various options for sharing prior results while chaining promise requests in this other answer How to chain and share prior results.


FYI, where this promise implementation really shines compare to what you show in your question is for error handling. Your non-promise code shows no error handling, but the promise version will propagate all errors up to the .catch() handler for you.