4

I am under the impression that these two things are not equivalent:

return somePromise()
  .then()
  .then()
  .then()
  .catch(function(e) { throw e; });

and

return somePromise()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });

The first snippet will only catch errors on the latest then (the other errors will be lost) whereas the second snippet will catch any error anywhere along the chain.

But I must be missing something because forcing the user to remember putting a catch after every promise defeats the purpose of promises.

Am I misunderstanding and placing a .catch() last will catch any error along the chain?

springloaded
  • 1,079
  • 2
  • 13
  • 23

3 Answers3

4

In the first case, if any of the then handlers are throwing an error, then that error will be caught at the last catch handler.

In the second case, if any of the then handlers throw an error, that error will go to the nearest catch handler. As you throw the error in catch handlers as well, it will simply go to the next nearest catch handler.

Am I misunderstanding and placing a .catch() last will catch any error along the chain?

Nope, catch at the end of the promise chain is the right thing to do in most of the cases. But, in case if you don't want to fail the entire chain of promises because of an intermediate failure, then you can return the value to be used by the next then handler in the catch handler.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • I see, that makes sense. Is throwing the error the right thing to do in a catch? If I don't catch anywhere, will the error then be swallowed or will it throw an exception anyway and I'm just giving up my chance to handle an error gracefully? – springloaded Oct 09 '15 at 16:54
  • 1
    @springloaded Think of the `catch` block as the `try...catch` block. What you normally do in the `try`'s `catch` block can be done in promise's `catch` block as well (normally logging the error, take corrective action etc). – thefourtheye Oct 09 '15 at 16:56
  • 1
    @springloaded - when you have a `.catch()` or the second function passed to a `.then()`, you have "handled" the promise error at that point and unless you return a rejected promise or throw an exception, the promise state of the chain will switch to fulfilled. So if you don't want the state to remain fulfilled, you have to throw or return a rejected promise. This allows you to decide from within your `.catch()` whether the promise chain should continue normal processing or not. As @thefourtheye said, it works like a try/catch block in this regard. – jfriend00 Oct 09 '15 at 17:34
2

Just adding to thefourtheye's great answer. Here is how your two code snippets look in synchronous code:

return somePromise()
  .then()
  .then()
  .then()
  .catch(function(e) { throw e; });

Becomes:

try {
    var val = someFunction();
    val = fn(val); // p.then(x => ...);
    //...
    return val;
} catch (e) {
    throw e; // this catch didn't actually do anything, but will be reached if someFunction
            // throws an error during execution
}

The second example:

return somePromise()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });
  .then()
  .catch(function(e) { throw e; });

Becomes, the not very interesting:

try { 
try {
   var val = someFunction();
   return val;
} catch (e) {
   throw e;
}
val = fn(val); // this is a then(); If it's really an empty then - a noop
} catch (e) {
   throw e;
}
val = fn(val); // this is a then(); If it's really an empty then - a noop
} catch (e) {
   throw e;
}
val = fn(val); // this is a then(); If it's really an empty then - a noop
} catch (e) {
   throw e; // yep, even this last one will be reached
}
}
Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • I disagree with your `the not very interesting:` ! in the second case, the first catch you can modify and update val to the next try but not in the first case, and this is very interesting. It depends of your needs ! – Anonymous0day Oct 11 '15 at 15:06
  • @Anonymous0day of course, but OP doesn't do anything, if you need to actually change things and rethrow it becomes relevant again. It's "not interesting" in tha tit's a noop in the above code. – Benjamin Gruenbaum Oct 11 '15 at 15:20
0

Some samples to have a better understanding of errors with promises.

You have to run the snippet and compare the result with the code to understand what happen.

In this sample we have :

  • 4 tests where we have a Promise chain in which we throw errors.
  • 4 ways to handle errors.

This sample try to show 4 different ways to handle the errors, and the result.

I hope this can help someone !

/*
* This part is only utility functions
* dont care about that
*/
var el = $('#dbg');
var fn = {
    log: function(val ) {
      el.append('<pre class="line">' + val + '</pre>');
      return val;
    },
    err : function(val, forced){
      var errNumber = forced ? val : 404;
      fn.log('<div class="thr">throwing an error : ' + errNumber + '</div>' );
      throw errNumber;
    },
    ok: function(val) {
      fn.log('<div class="ok">received : ' + val + ' | returning : ' + (val+1) + '</div>');
      return val+1;
    },
    ko: function(val) {
      fn.log('<div class="ko">received : ' + val + ' | returning : ' + (val-1) + '</div>');
      return val-1;
    },
    catch : function(val){
      fn.log('<div class="ko">FROM CATCH : \treceived : ' + val + ' | returning : ' + val + '</div>');
      return val;
    
    },
    sep : function(val){
      fn.log('<div class="sep">&nbsp;</div>');
      return val;
    
    },
    
    };
                

fn.log('Each fn.ok increment + 1 => fn.ok(5) : log 5 in a green line and return 6');
fn.ok(5);
fn.log('');
fn.log('Each fn.ko decrement - 1 => fn.ko(5) : log 5 in a red line and return 4');
fn.ko(5);




/*
*
* Each fn.ok increment + 1
* Each fn.ko decrement - 1
*
*/


/*
* Test 1 :
*
*    only one catch at end
*
*/
var p = Promise.resolve()
  .then(function(){
    
    var val = 1;
    
    fn.sep();
    fn.log('start test : ' + val);
    fn.log('\n\tonly one catch at end\n<hr>');
    fn.log('Promise.resolve(1)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t  .then(fn.err)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.catch(fn.catch)');
    
    return val;
  })
  .then(fn.ok)
  .then(fn.ok)
  .then(fn.ok)
    .then(fn.err)
  .then(fn.ok)
  .then(fn.ok)
  .then(fn.ok)
  .catch(fn.catch)
  ;

/*
* Test 2 :
*
*    same as test 1
*    only one catch at end
*    but we start by an error
*
*/
p = p.then(function(){
    
    var val = 2;  
    
    fn.sep();
    fn.log('start test : ' + val);
    fn.log('\n\tsame as test 1\n\tonly one catch at end\n\tbut we start by an error\n<hr>');
    fn.log('Promise.resolve()\n\t  .then(fn.err)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t  .then(fn.err)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.then(fn.ok)\n\t.catch(fn.catch)');
 
    
    return fn.err();
  })
  .then(fn.ok)
  .then(fn.ok)
  .then(fn.ok)
    .then(fn.err)
  .then(fn.ok)
  .then(fn.ok)
  .then(fn.ok)
  .catch(fn.catch)
  ;

/*
* Test 3 :
*
*    same as test 2
*    we start by an error
*    but each one is chained
*    to a catcher
*
*/
p = p.then(function(){
    
    var val = 3;  
    
    fn.sep();
    fn.log('start test : ' + val);
    
    fn.log('\n\tsame as test 2\n\twe start by an error\n\tbut each one is chained\n\tto a catcher\n<hr>');
    fn.log('Promise.resolve('+val+')\n\t  .then(fn.err)\n\t.then(fn.ok).catch(fn.catch)\n\t.then(fn.ok).catch(fn.catch)\n\t.then(fn.ok).catch(fn.catch)\n\t  .then(fn.err)\n\t.then(fn.ok).catch(fn.catch)\n\t.then(fn.ok).catch(fn.catch)\n\t.then(fn.ok).catch(fn.catch)\n\t.catch(fn.catch)');  
  
    return fn.err(val , true);
  })
  .then(fn.ok).catch(fn.catch)
  .then(fn.ok).catch(fn.catch)
  .then(fn.ok).catch(fn.catch)
    .then(fn.err)
  .then(fn.ok).catch(fn.catch)
  .then(fn.ok).catch(fn.catch)
  .then(fn.ok).catch(fn.catch)
  .catch(fn.catch)
  ;

/*
* Test 4 :
*
*    same as test 2
*    we start by an error
*    but each one have
*    a rejected handler
*
*/

p = p.then(function(){
    
    var val = 4;  
    
    fn.sep();
    fn.log('start test : ' + val);
    fn.log('\n\tsame as test 2\n\twe start by an error\n\tbut each one have\n\ta rejected handler\n<hr>');

    fn.log('Promise.resolve('+val+')\n\t  .then(fn.err)\n\t.then(fn.ok , fn.ko)\n\t.then(fn.ok , fn.ko)\n\t.then(fn.ok , fn.ko)\n\t  .then(fn.err , fn.ko)\n\t.then(fn.ok , fn.ko)\n\t.then(fn.ok , fn.ko)\n\t.then(fn.ok , fn.ko)\n\t.catch(fn.catch)');
  
    return fn.err(val , true);
  })
  .then(fn.ok , fn.ko)
  .then(fn.ok , fn.ko)
  .then(fn.ok , fn.ko)
    .then(fn.err , fn.ko)
  .then(fn.ok , fn.ko)
  .then(fn.ok , fn.ko)
  .then(fn.ok , fn.ko)
  .catch(fn.catch)
  ;
.line{
  border:solid 1px transparent;
  margin : 3px;
  padding : 3px;
  }
.line .ok,
.line .ko,
.line .thr{
  margin-left : 24px;
  padding-left : 3px;
  }
.ok{
  background : #9F9;
  }
.ko{
  background : #F99;
  }
.thr{
  background : #666;
  color : #DDD;
  }
.sep{
  border:solid 1px #666;
  background : #CCF;
  border-radius : 12px;
  margin-top : 21px;
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://raw.githubusercontent.com/jakearchibald/es6-promise/master/dist/es6-promise.min.js"></script>


<div id='dbg'></div>
Anonymous0day
  • 3,012
  • 1
  • 14
  • 16
  • 1
    I have no idea what this answer is trying to show. Your words need to explain why this is an answer. – jfriend00 Oct 09 '15 at 20:24
  • I had completed the goal of my intervention. have you run the code snippet and read the code ? it is a simple test demonstration. – Anonymous0day Oct 10 '15 at 05:01
  • 1
    I ran the snippet and and had no idea what I was looking at or how it was relevant to the question. Your answer simply does not explain that. – jfriend00 Oct 10 '15 at 05:02
  • I am so sorry but this sample try to show 4 different ways to handle the errors, and the result. The aim is to have a better understanding of errors with promises. How can i improve my answer ? – Anonymous0day Oct 10 '15 at 05:16
  • @jfriend00 : i have made some improvements, hoping it become more understandable – Anonymous0day Oct 10 '15 at 09:55