springloaded
springloaded

Reputation: 1079

How to properly catch errors in promises?

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?

Upvotes: 4

Views: 1859

Answers (3)

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276286

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

Upvotes: 2

Anonymous0day
Anonymous0day

Reputation: 3042

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>

Upvotes: 0

thefourtheye
thefourtheye

Reputation: 239443

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.

Upvotes: 4

Related Questions