7

I don't necessarily want to error, but I have:

getFromDb().then (tradeData) ->
  if not tradeData
    # DO NOT CONTINUE THE CHAIN
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err

Any way for me to abort the chain if there is no tradeData?

Shamoon
  • 41,293
  • 91
  • 306
  • 570
  • Can you not return a new promise explicitly (one that is resolve-failed as appropriate) from `then` in bb? If just "failing the chain" doesn't work then .. it's not a chain at all. – user2864740 Feb 17 '15 at 22:19
  • You'll want to have a look at [Break promise chain and call a function based on the step in the chain where it is broken](http://stackoverflow.com/q/20714460/1048572) and [Handling multiple catches in promise chain](http://stackoverflow.com/q/26076511/1048572) – Bergi Feb 17 '15 at 23:55

3 Answers3

8

Although an accepted answer, but I would like to tell all of googlers that, "break()" function has been changed to "cancel()"

Use Something like this:

p = getFromDb().then (tradeData) ->
  if not tradeData
    send("no data");
    p.cancel(); // Look Here!!!!!!!!!!!!!!!!
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err

Before that, make sure to add following lines in config:

Promise.config({
    cancellation: true
});
Sahil Purav
  • 1,324
  • 4
  • 19
  • 32
7
getFromDb().then (tradeData) ->
  if tradeData
    getLatestPrice tradeData ->
      .then (latestPrice) ->
        ...
      .then ->
        ...
      .then ->
        ...
      .catch (err) ->
        next err
  else
    getSomethingElse ->
       send("no data")

In 3.0, you will be able to do this:

p = getFromDb().then (tradeData) ->
  if not tradeData
    send("no data");
    p.break()
  else
    getLatestPrice tradeData
.then (latestPrice) ->
  ...
.then ->
  ...
.then ->
  ...
.catch (err) ->
  next err
Esailija
  • 138,174
  • 23
  • 272
  • 326
  • 1
    What if I want to implement additional branching logic? For example, if there is `tradeData` do certain steps, if there isn't, do other steps? – Shamoon Feb 17 '15 at 23:10
  • 1
    @Shamoon use an `else` – Esailija Feb 17 '15 at 23:11
  • @Shamoon: Of course, you can also `throw` an "no data" exception and [use rejections for the control flow](http://stackoverflow.com/a/24663315/1048572). This will especially "not continue the chain" as you requested – Bergi Feb 17 '15 at 23:37
  • @Bergi plz just wait for 3.0 :D – Esailija Feb 17 '15 at 23:39
  • 1
    @Esailija: Why? I hope you haven't build a `goto` into promise chains? Edit: Oops, I've read your answer now. Of course, cancelling yourself is not as clear :-) – Bergi Feb 17 '15 at 23:42
  • @Bergi nope, just `break` like there is `break label` in javascript already. It is actually just an alias for `cancel()` but it can be used as break as shown – Esailija Feb 17 '15 at 23:43
  • 2
    @Bergi incidentally in 3.0 you can use object properties as error predicates so you could have `throw {label: "no data"}` and then a `.catch({label: "no data"}, function(e){})` later - but I would still in this particular case prefer break as it should be pretty intuitive what happens here. – Esailija Feb 17 '15 at 23:47
  • What's break doing here? – Benjamin Gruenbaum Feb 18 '15 at 07:26
  • 3
    @Esailija : break is not mentioned in bluebird API http://bluebirdjs.com/docs/api-reference.html – Manish Trivedi Nov 06 '15 at 09:23
  • @ManishTrivedi it's called `cancel()` now : http://bluebirdjs.com/docs/api/cancellation.html – Shanoor May 09 '16 at 08:54
0

Im just wondering why not taking a benefit of fact that you can throw whatever you like, not just something that is instanceof Error. It is considered as bad practice to do that? In my opinion it depends, it depends on what you are trying to accomplish. Promise chain can be interrupted by various reasons, but in general those two would fall into two groups. Classic error occur and early break in chain needed. Logically second one can't be considered as something that should be an instance of Error.

const handleError = (err) => {
  ...
}

const skip = (reason, ..., ...) => {
  /**
   * construct whatever you like
   * just for example here return reason
   */
  return reason
}

Promise.resolve()
.then(() => {
  if (iShouldEndChainEarlier) {
    throw skip('I would like to end chain earlier')
  }

  return asyncOperation1()
})
.then(results => {
  ...
  return asyncOperation2(results)
})
.then(... => {
  ...
})
.catch(interrupt => {
  if (interrupt instanceof Error) {
    return handleError(interrupt)
  }

  /**
   * Handle breaking promise chain earlier
   * having interrupt reason in scope
   */
})

If logically, early break in chain can be considered as error(which can totally be the case), you could create your custom error and differentiate between the two in catch block. So just saying that one could think about another approach(s) when handling whatever interrupt that can occur in promise chain.

We could argue does this can be considered as something against first error pattern in node. If there is an error best practice would be to invoke callback like callback(err) where err really should be instanceof Error otherwise callback(null, data). But on other side having in mind that .catch(fn) is just sugar for then(undefined, onRejected) to me it seems good enough to handle onRejected parameter based on situation you are in.

Srle
  • 10,366
  • 8
  • 34
  • 63