121

I found the following code in a tutorial:

promise.then(function(result){
    //some code
}).catch(function(error) {
    throw(error);
});

I'm a bit confused: does the catch call accomplish anything? It seems to me that it doesn't have any effect, since it simply throws the same error that was caught. I base this on how a regular try/catch works.

Samrat Hasan
  • 313
  • 1
  • 4
  • 8
Tyler Durden
  • 2,031
  • 4
  • 20
  • 25
  • Could you provide a link to the tutorial? Maybe there is additional context that would be helpful... – Igor Feb 10 '17 at 19:32
  • @Igor I can't, it's on Pluralsight. Is this possibly just a placeholder for some error handing logic? – Tyler Durden Feb 10 '17 at 19:36
  • That's what I would guess as it does nothing more then pass the error along to the caller which could also be accomplished by not having the catch to begin with. – Igor Feb 10 '17 at 19:37
  • 1
    @TylerDurden I suspect that you're correct about it being a placeholder. – Jared Smith Feb 10 '17 at 19:49
  • @TylerDurden, I'd also guess that it is a placeholder. Maybe trying to demonstrate how to format/normalize errors. Basically the promise-equivalent to `try { ... }catch(error){ throw new Error("something went wrong") }`. Or to show that Promises and Errors are compatible *(at least that way around)*. But in its current implementation it's just stupid. You're right, it doesn't do anything and it's not even like a hook you'd add in OOP to enable overwriting it in an inheriting class. I'd add the catch-block as soon as it does something, but not like that, not just as a placeholder. – Thomas Feb 10 '17 at 20:09

6 Answers6

175

There is no point to a naked catch and throw as you show. It does not do anything useful except add code and slow execution. So, if you're going to .catch() and rethrow, there should be something you want to do in the .catch(), otherwise you should just remove the .catch() entirely.

The usual point for that general structure is when you want to execute something in the .catch() such as log the error or clean up some state (like close files), but you want the promise chain to continue as rejected.

promise.then(function(result){
    //some code
}).catch(function(error) {
    // log and rethrow 
    console.log(error);
    throw error;
});

In a tutorial, it may be there just to show people where they can catch errors or to teach the concept of handling the error, then rethrowing it.


Some of the useful reasons for catching and rethrowing are as follows:

  1. You want to log the error, but keep the promise chain as rejected.
  2. You want to turn the error into some other error (often for easier error processing at the end of the chain). In this case, you would rethrow a different error.
  3. You want to do a bunch of processing before the promise chain continues (such as close/free resources) but you want the promise chain to stay rejected.
  4. You want a spot to place a breakpoint for the debugger at this point in the promise chain if there's a failure.
  5. You want to handle a specific error or set of errors, but rethrow others so that they propagate back to the caller.

But, a plain catch and rethrow of the same error with no other code in the catch handler doesn't do anything useful for normal running of the code.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • From my opinion it is noe\t good example. With such approach you easy get multiple logging for 1 error. In java you can just `throw new Exception(periousException);` I do not know whether javascript supports nested error, but anyway "log and throw" is bad practise. – Cherry Sep 11 '17 at 13:36
  • 35
    @Cherry - You cannot say that this is a bad practice in general. There are times when a module wants to log its own errors in its own way and this is one way to do that. Besides, I'm not recommending this, I'm just explaining that there's no reason to have a `.catch()` and throw the same error inside the catch unless you do SOMETHING else in the `.catch()`. That's the point of this answer. – jfriend00 Sep 11 '17 at 21:12
  • 2
    Generally exceptions should fit the level of abstraction. Its perfectly OK to catch a db related exception for example, and throw something like a "service" exception which will be handled by the caller. This especially useful when you don't which to expose details about low-level exceptions – maxTrialfire Apr 17 '18 at 00:51
  • @maxTrialfire - Yes, I agree. The question was about catching and rethrowing the same error without doing anything else. If you want catch it and turn it into some other error, that is certainly a legit reason for catch and rethrow. – jfriend00 Apr 17 '18 at 00:55
  • I don't mind a naked catch and throw if I want to document the fact that the catch is being handled elsewhere. – Elliott Aug 23 '18 at 20:35
  • 3
    Another good reason to catch and (sometimes) throw is to handle a specific error, but rethrow everything else. – Jasper Oct 29 '18 at 10:28
  • If you only want to free resources you can do it in a `.finally`. In my opinion this is the cleaner way to do it. (See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally) – Simon Zyx May 13 '20 at 13:40
  • 2
    @SimonZyx - Yes, `.finally()` can be very useful for that, but sometimes the resources are already taken care of in the non-error path so `.catch()` is still the place to close them. It really depends upon the situation. – jfriend00 May 13 '20 at 15:35
  • Using throw in this situation is bad practice. You should not do `throw error`. Instead you should `return Promise.reject(error)`. see discussion https://stackoverflow.com/questions/33445415/javascript-promises-reject-vs-throw – John Henckel Dec 10 '21 at 17:01
30

Both .then() and .catch() methods return Promises, and if you throw an Exception in either handler, the returned promise is rejected and the Exception will be caught in the next reject handler.

In the following code, we throw an exception in the first .catch(), which is caught in the second .catch() :

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
});

The second .catch() returns a Promised that is fulfilled, the .then() handler can be called :

new Promise((resolve, reject) => {
    console.log('Initial');

    resolve();
})
.then(() => {
    throw new Error('Something failed');
        
    console.log('Do this'); // Never reached
})
.catch(() => {
    console.log('Something failed');
    throw new Error('Something failed again');
})
.catch((error) => {
    console.log('Final error : ', error.message);
})
.then(() => {
    console.log('Show this message whatever happened before');
});

Useful reference : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#Chaining_after_a_catch

Hope this helps!

Philippe Sultan
  • 2,111
  • 17
  • 23
4

There is no important difference if you leave out the catch method call completely.

The only thing it adds is an extra microtask, which in practice means you'll notice the rejection of the promise later than is the case for a promise that fails without the catch clause.

The next snippet demonstrates this:

var p;
// Case 1: with catch
p = Promise.reject('my error 1')
       .catch(function(error) {
          throw(error);
       });

p.catch( error => console.log(error) );
// Case 2: without catch
p = Promise.reject('my error 2');

p.catch( error => console.log(error) );

Note how the second rejection is reported before the first. That is about the only difference.

trincot
  • 317,000
  • 35
  • 244
  • 286
3

So it sounds like your question is, "In the promise chain, what does the .catch() method do?"

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw

The throw statement "will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate."

In the promise chain, the .then() method will return some type of data chunk. This return of the chunk will complete the promise. The successful return of the data completes the promise. You can think of the .catch() method in the same way. .catch() however will handle unsuccessful data retrieves. The throw statement completes the promise. Occasionally, you will see developers use .catch((err) => {console.log(err))} which would also complete the promise chain.

Brad
  • 159,648
  • 54
  • 349
  • 530
0

You actually don't need to re throw it, just leave the Promise.catch empty otherwise it will consider as un handle the reject and then wrap the code in a try catch and it will catch the error automatically which is passing down.

try{
  promise.then(function(result){
    //some code
  }).catch(function(error) {
    //no need for re throwing or any coding. but leave this as this otherwise it will consider as un handled
  });
}catch(e){
  console.log(e);
  //error can handle in here
}
Aylian Craspa
  • 422
  • 5
  • 11
  • 1
    I think, that have catch empty will silently ignore exception, which is different to OP scenario, that propagate exception to the next level – Michael Freidgeim May 04 '21 at 14:19
0

In the promise chain, it is better to use .catch

ex in function f2: .then(...).catch(e => reject(e));

  • test1 - with try catch
  • test2 - without try or .catch
  • test3 - with .catch

function f1() {
    return new Promise((resolve, reject) => {
        throw new Error('test');
    });
}

function f2() {
    return new Promise((resolve, reject) => {
        f1().then(value => {
            console.log('f1 ok ???');
        }).catch(e => reject(e));
    });
}

function test1() {
    console.log('test1 - with try catch - look in F12');
    try {
      f2().then(() => { // Uncaught (in promise) Error: test
        console.log('???'); });
    } catch (e) {
      console.log('this error dont catched');
    }
}

function test2() {
    console.log('test2 - without try or .catch - look in F12');
    f2(); // Uncaught (in promise) Error: test
}

function test3() {
  console.log('test3 - with .catch');
  f2().then(value => {
    console.log('??');
  }).catch(e => {
    console.log(' now its ok, error ', e);
  })
}

setTimeout(() => { test1(); 
  setTimeout(() => { test2(); 
    setTimeout(() => { test3(); 
    }, 100);
  }, 100);
}, 100);
Wagner Pereira
  • 1,050
  • 11
  • 11