83

Is there any way to globally catch all exceptions including Promise exceptions. Example:

    window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
        alert("Error occured: " + errorMsg);//or any message
        return false;
    }

    var myClass = function(){

    }


    var pr = new Promise(function(resolve, react){
        var myInstance = new myClass();
        myInstance.undefinedFunction(); // this will throw Exception
        resolve(myInstance);
    });


    pr.then(function(result){
        console.log(result);
    });

    // i know right will be this:
    // pr.then(function(result){
    //     console.log(result);
    // }).catch(function(e){
    //     console.log(e);
    // });

This script will silently die without error. Nothing in firebug.

My question is if I do a mistake and forgot to catch it is there any way to catch it globally?

Satpal
  • 132,252
  • 13
  • 159
  • 168
John
  • 2,494
  • 5
  • 21
  • 24

5 Answers5

48

Update, native promises now do the following in most browsers:

window.addEventListener("unhandledrejection", function(promiseRejectionEvent) { 
    // handle error here, for example log   
});

We were just discussing this the other day.

Here is how you'd do this with bluebird:

window.onpossiblyunhandledexception = function(){
    window.onerror.apply(this, arguments); // call
}

window.onerror = function(err){
    console.log(err); // logs all errors
}

With Bluebird it's also possible to use Promise.onPossiblyUnhandledRejection. The calls for done are not needed as the library will detect unhandled rejection itself unlike Q (UPDATE 2016 - I now wrote code for Q and it does this).

As for native promises - they will eventually report to either window.onerror or a new handler but the specification process is not yet done - you can follow it here.

Benjamin Gruenbaum
  • 270,886
  • 87
  • 504
  • 504
  • > in most browsers: [According to MDN](https://developer.mozilla.org/en-US/docs/Web/Events/unhandledrejection), the only browser that supports this as of June 2018 is Chrome. – John Hatton Jun 27 '18 at 15:58
  • 1
    @JohnHatton according to [statcounter](http://gs.statcounter.com/browser-market-share) most browsers are Chrome. That said - you can always use a promise library in order to get this behavior. I pinged people in Firefox (which has it under a flag) Safari and Edge to see if we can do this in more browsers soon. – Benjamin Gruenbaum Jun 27 '18 at 17:31
  • 2
    "most browsers are Chrome" OK, I suppose "the browser used in most sessions" is one definition of "most browsers"... not the one I would pick. – John Hatton Jun 27 '18 at 22:05
  • It looks like with latest version of bluebird, all you need to do is basically `window.addEventListener("unhandledrejection", function(event) {})` http://bluebirdjs.com/docs/api/error-management-configuration.html#global-rejection-events -- I may be mis-reading the docs, but it sounds like bluebird will now trigger a `unhandledrejection` event for you – Devin Rhode May 03 '21 at 22:49
  • @DevinRhode yeah we do that now – Benjamin Gruenbaum May 04 '21 at 08:58
  • This doesn't prevent the Promise.reject from creating the uncaught error notification in the console (Firefox). – David Spector Jun 16 '21 at 12:10
  • @DavidSpector that's unfortunate - open a bug? – Benjamin Gruenbaum Jun 16 '21 at 12:34
  • Yes, when I use an unhandledrejection handler, it catches only one of the two notifications shown in the Console. The other may be a bug. I should investigate and compare with Chrome when I have more time. – David Spector Jun 16 '21 at 16:14
16

Most promise implementations don't currently provide the type of functionality you are referring to, but a number of 3rd-party promise libraries (including Q and bluebird) provide a done() method that will catch and rethrow any uncaught errors, thus outputting them to the console.

(Edit: see Benjamin Gruenbaum's answer about Bluebird's features for handling uncaught exceptions)

So if you have that, you'd just need to follow the rule of thumb that any promise you use should either be returned or terminated with .done():

pr.then(function(result){
    console.log(result);
})
.done();

To quote from the Q API reference:

The Golden Rule of done vs. then usage is: either return your promise to someone else, or if the chain ends with you, call done to terminate it. Terminating with catch is not sufficient because the catch handler may itself throw an error.

I realize that this still requires a bit of extra work and you can still forget to do this, but it is an improvement over having to .catch() and explicitly handle every error, especially for the reason pointed out above.

JLRishe
  • 99,490
  • 19
  • 131
  • 169
  • 2
    Actually done is not any different from simply doing `.catch(thrower)` (or doing the same with shortcut e.g. adding `Promise.prototype.done = ...`. So it is pretty pointless for this purpose, especially in bluebird where uncaught errors are reported normally. – Esailija Jan 18 '15 at 17:52
  • 1
    @Esailija Can you elaborate on that? What would `thrower` be in this case? – JLRishe Jan 18 '15 at 17:58
  • `function thrower(e){setTimeout(function(){throw e}, 0);}` – Esailija Jan 18 '15 at 17:59
  • 1
    @Esailija Ok, well it may be unneeded in Bluebird, but it's still the simplest way to prevent errors from getting completely swallowed in some other implementations (AFAIK), so I wouldn't call it pointless. – JLRishe Jan 18 '15 at 18:08
  • 1
    @JLRishe I think his point is that people should not be using promise implementations that swallow errors (like jQuery, Q, $q etc) and use promise libraries that allow you to track unhandled rejections like Bluebird, When, RSVP etc. – Benjamin Gruenbaum Jan 18 '15 at 18:21
  • 1
    I meant pointless as a separate concept other than glorified catch settimeout throw – Esailija Jan 18 '15 at 18:30
6

In Node.js, you can use:

process.on('unhandledRejection', (reason, promise) => {
  console.error(`Uncaught error in`, promise);
});
JussiR
  • 2,055
  • 3
  • 21
  • 23
  • This is useful when debugging in the unknown but if you know which promise and have access, you should use `.catch` or `.done` for better maintainability – Taku Mar 16 '17 at 04:43
  • In newer version of Node this has actually become the standard behaviour. So errors in uncaught promises will be logged, even without adding this piece of code. – JussiR Mar 17 '17 at 11:25
  • How could you get the promise that caused this? or the function file? The thing is that reason.stack is always undefined. – Loebre Dec 06 '18 at 17:09
  • Using a promise (or setTimeout) creates a new stack, so you can't see where the promise was called from. Though newer version of Chrome (V8?) allow you to see previous stacks as well helping a lot in debugging asynchronous code. – JussiR Dec 22 '20 at 07:34
2

If you're writing code that can be executed both in a browser & in a Node.js environment, you could wrap your code in a self-executing function like this :

var isNode = (typeof process !== 'undefined' && typeof process.on !== 'undefined');
(function(on, unhandledRejection) {
    // PUT ANY CODE HERE
    on(unhandledRejection, function(error, promise) {
        console.error(`Uncaught error in`, promise);
    });
    // PUT ANY CODE HERE
})(
    isNode ? process.on.bind(process) : window.addEventListener.bind(window),
    isNode ? "unhandledRejection" : "unhandledrejection"
);

What would happen if you use this code :

  • If you'd run it in a Node.js environment, your handler would be attached to the process object and be executed when you have an uncaught exception in a promise.

  • If you'd run it in a browser environment, your handler would be attached to the window object and be executed when you have an uncaught exception in a promise and your browser supports the unhandledrejection event.

  • If you'd run it in a browser environment without support for the unhandledrejection, you will not be able to catch your uncaught exception and your unhandledrejection event handler will never be triggered, but you would not get any errors if there's no uncaught exceptions.

John Slegers
  • 45,213
  • 22
  • 199
  • 169
0

If you are using native Promise, it's pretty simple.
You only need to .catch this reject some where.

ajax(request).catch(function(rejected){
      console.log(rejected);
});

If I don't catch it somewhere, the uncaught in promise will keep showing. But If I catch it somewhere...

showdev
  • 28,454
  • 37
  • 55
  • 73
redyyu
  • 41
  • 1