65

So, I want my first level catch to be the one that handles the error. Is there anyway to propagate my error up to that first catch?

Reference code, not working (yet):

Promise = require('./framework/libraries/bluebird.js');

function promise() {
    var promise = new Promise(function(resolve, reject) {
        throw('Oh no!');
    });

    promise.catch(function(error) {
        throw(error);
    });
}

try {   
    promise();
}
// I WANT THIS CATCH TO CATCH THE ERROR THROWN IN THE PROMISE
catch(error) {
    console.log('Caught!', error);
}
Kirk Ouimet
  • 27,280
  • 43
  • 127
  • 177
  • 15
    Just so you know, you're asking for confusion to have two different things named `promise` (a function and a variable), not to mention the built-in `Promise`. I'm sure you could make it work, but why make your code confusing like that? – jfriend00 Jul 27 '14 at 03:28

5 Answers5

56

You cannot use try-catch statements to handle exceptions thrown asynchronously, as the function has "returned" before any exception is thrown. You should instead use the promise.then and promise.catch methods, which represent the asynchronous equivalent of the try-catch statement. (Or use the async/await syntax noted in @Edo's answer.)

What you need to do is to return the promise, then chain another .catch to it:

function promise() {
    var promise = new Promise(function(resolve, reject) {
        throw('Oh no!');
    });

    return promise.catch(function(error) {
        throw(error);
    });
}

promise().catch(function(error) {
    console.log('Caught!', error);
});

Promises are chainable, so if a promise rethrows an error, it will be delegated down to the next .catch.

By the way, you don't need to use parentheses around throw statements (throw a is the same as throw(a)).

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
  • 1
    @Kirk: I'd only do so if you can't edit this `promise` function, as it's much more consistent to use one at a time. In that case, I've edited my answer to add a link to a possible solution. – Qantas 94 Heavy Jul 27 '14 at 03:43
  • 2
    For what it's worth, you should definitely _not_ use the method in my answer there and you should use promises instead. Promises already solve this problem and provide throw safety. You can simply omit the `promise.catch`, in fact, the whole code above can be refactored to `Promise.reject("oh no")` (although you should _always_ reject with errors). Then you can do `Promise.reject("oh no").catch(function(e){ console.log(e); });` which logs "Oh no". Throw safety is in fact one of the strongest selling points of promises alongside chainability and composability. – Benjamin Gruenbaum Jul 27 '14 at 06:10
  • @BenjaminGruenbaum: I was assuming that it was just a mockup example based on some other code that sometimes (re)throws an error, not that this was the exact code. In any case, I've edited my answer to more strongly emphasise the use of promsies. – Qantas 94 Heavy Jul 27 '14 at 06:17
46

With the new async/await syntax you can achieve this. Please note that at the moment of writing this is not supported by all browsers, you probably need to transpile your code with babel (or something similar).

// Because of the "async" keyword here, calling getSomeValue()
// will return a promise.
async function getSomeValue() {
  if (somethingIsNotOk) {
    throw new Error('uh oh');
  } else {
    return 'Yay!';
  }
}

async function() {
  try {
    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);
  } catch (error) {
    console.error(error);
  }
}
Edo
  • 3,311
  • 1
  • 24
  • 25
  • if a function is returning Promise do we need to declare that method as async. – j10 Jul 19 '17 at 16:42
  • You only need the async keyword if you want to use `await` inside that function. Using the async keyword will have as a side effect that the promise will return a promise. If you want to await the result of a function X which is returning a promise to begin with, making function X async will have no effect. – Edo Jul 20 '17 at 13:13
  • missing: declaring `Promise.reject(err);` inside catch https://stackoverflow.com/a/37993829/4933053 – droid192 Mar 31 '18 at 10:09
  • 2
    @ItsmeJulian That depends on how you want to handle it. If you just want to log inside the catch, then you won't need to bubble the error up. Also, I think I'd prefer `throw err` inside catch if you want to bubble it up. – Edo Apr 03 '18 at 13:19
5

No! That's completely impossible, as promises are inherently asynchronous. The try-catch clause will have finished execution when the exception is thrown (and time travel still will not have been invented).

Instead, return promises from all your functions, and hook an error handler on them.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
4

I often find the need to ensure a Promise is returned and almost as often needing to handle a local error and then optionally rethrow it.

function doSomeWork() {
  return Promise.try(function() {

    return request.get(url).then(function(response) {
      // ... do some specific work
    });

  }).catch(function(err) {
    console.log("Some specific work failed", err);
    throw err; // IMPORTANT! throw unless you intend to suppress the error
  });
}

The benefit of this technique (Promise.try/catch) is that you start/ensure a Promise chain without the resolve/reject requirement which can easily be missed and create a debugging nightmare.

kingdango
  • 3,979
  • 2
  • 26
  • 43
2

To expand on edo's answer, if you want to catch the errors of an async function that you don't want to wait for. You can add an await statement at the end of your function.

(async function() {
  try {
    const asyncResult = someAsyncAction();

    // "await" will wait for the promise to resolve or reject
    // if it rejects, an error will be thrown, which you can
    // catch with a regular try/catch block
    const someValue = await getSomeValue();
    doSomethingWith(someValue);

    await asyncResult;
  } catch (error) {
    console.error(error);
  }
})();

If someAsyncAction fails the catch statement will handle it.

  • Just a note: this will still halt the completion of the outer function until asyncResult finishes (either with an error, or successfully). If you don't want to wait on asyncResult at all, chaining a `.catch` on `someAsyncAction()` is probably easier. – Edo Oct 23 '18 at 10:16
  • I'm not sure what you mean when you say it would halt the completion of the outer function, but it would not block the event loop. As it's written, execution of any code placed after the outer function would not be blocked by the resolution of the promises inside the function. The only thing you would gain by adding a `.catch` to `someAsyncAction()` is that the error handling would be bound synchronously. This would avoid extraneous `UnhandledPromiseRejectionWarning` messages, if `someAsyncAction` were to reject before the `await asyncResult` line was reached. – Jonathan Felchlin Oct 24 '18 at 20:52
  • 1
    I mean that if some code is `await`-ing the outer function, that code still has to wait on `someAsyncAction` to complete. Very often one might want to trigger some async action, but not wait on the promise to resolve/error, for example sending analytics data. Now when chaining the `.catch` (and omitting the `await asyncResult` entirely), you can still handle async errors on `someAsyncAction`, while making sure no other logic is waiting on it. – Edo Oct 25 '18 at 09:39