2

For example,

Comments.findOne({user: req.user.id}).exce()
.then(function(comment) {
  if(comment) {
    // how to make this return immediately and break the rest then?
    return res.json({error: 'Already commented'});
  } else {
    return Posts.findOne({postId: req.params.id}).exec();
  }
})
.then(function(post) {
  if(post) {
    var comment = new Comment({user: req.user.id, data: req.body.comment})
    return {post: post, comment: comment.save()};
  } else {
    return res.json({error: 'Post not exist'});
  }
})
.then(function(data) {
  post.comments.push(comment._id);
  return post.save();
});
.then(function(post) {
  return res.json({ok: 1})
})
.catch(function(e)) {
  return res.json(error: e);
});

Is this promise written right? How to write this kind of promise? Callbacks/Promises is a headache...

Sato
  • 8,192
  • 17
  • 60
  • 115
  • 1
    Possible duplicate of [how to break promise chain](http://stackoverflow.com/questions/28803287/how-to-break-promise-chain) – mido Mar 17 '16 at 07:10
  • What does "return response to client in the middle of promise" mean? What exactly are you trying to accomplish? – jfriend00 Mar 17 '16 at 08:12
  • FYI, there's an extraneous `;` before the last `.then()` in your code which will cause an error. This will not be as likely to happen if you code like `}).then()` rather than putting the `.then()` on the next line by itself. – jfriend00 Mar 17 '16 at 08:12
  • OK, I finally found what you're talking about. Your question is not very obvious when you bury the explanation in a comment in the code. We are more likely to follow what you are asking if the words outside the code explain the problem or at least refer to the relevant comment in the code. I've provided an answer which I think addresses that comment in your code. – jfriend00 Mar 17 '16 at 08:22

2 Answers2

3

You're using bluebird, it supports cancellation. Here's an example:

var Promise = require('bluebird');
// enable cancellation
Promise.config({
    cancellation: true
});

// store your promise chain
var myPromise = Promise.resolve().then(() => {
    return 'foo';
}).then((res) => {
    console.log(res);
    // call cancel on the chain when needed
    myPromise.cancel();
    return res;
}).then((res) => {
    // this will not be executed
    console.log(res + '2');
});
Shanoor
  • 13,344
  • 2
  • 29
  • 40
  • The OP isn't trying to cancel a promise. They're trying to break the `.then()` chain which is typically done by just rejecting (either throwing or returning a rejected promise). That's how you abort a chain of promises and trigger error handling instead. – jfriend00 Mar 17 '16 at 08:17
2

You just need to throw or return a reject promise to trigger the error handling in promises as show in this example:

Comments.findOne({user: req.user.id}).exce()
.then(function(comment) {
  if(comment) {
    // to bypass all the other .then() resolve handlers, either
    // throw an error here or return a rejected promise
    throw new Error(res.json({error: 'Already commented'}));
  } else {
    return Posts.findOne({postId: req.params.id}).exec();
  }
})

Promises are "throw safe" which means that .then() will catch any exception thrown and turn it into a rejected promise. This will bypass any following .then() resolve handlers and will instead go to the next reject handler or .catch() handler.


FYI, you should be careful in your code because when you .catch() and then just return from that, it changes your promise state from rejected to resolved and it will then look like a successful promise when you actually had an error and any caller will think everything was successful. This is because the promise infrastructure assumes that if you .catch() an error and return a value that you have "handled" the error and the state is now resolved successfully. To allow the error to continue to propagate to higher callers from a .catch(), you have to re throw or return a rejected promise:

blah(...).then(...)
.catch(function(e)) {
     throw new Error(res.json(error: e));
});
jfriend00
  • 683,504
  • 96
  • 985
  • 979