26

For error reporting, I would like to insert a try-catch wrapper around the code of every function I have.

So basically I want to replace

function foo(arg){
   bar();
}

...with...

function foo(arg){
    try {
        bar() 
    }
    catch(e){
        customErrorHandler(e)
    }
}

Is there a way to apply this generic try-catch thing to all functions without manually editing all of them? For example by modifying the prototype of the Function object?

EDIT

Why I want to try-catch all my functions: I am building an HTML5 app that I'm publishing on iOS and Android. I can tell from my current rudimentary javascript error reporting that even though the app runs nicely on my own device, errors do occur on some other devices.

My objective is twofold: whenever a javascript error occurs on someone's device...

  1. I want to notify the user that the app may not function perfectly
  2. I want to know roughly where the error occurred, so I know where to look for the problem
Wytze
  • 7,844
  • 8
  • 49
  • 62
  • 1
    No - you could merely wrap the function's *invocation* in a try-catch, but not its contents if the function does not actually contain a try-catch. For what it's worth, blanketing your code with try-catch statements is a pretty bad idea. If there's an error, you should resolve it, not allow it to happen but suppress it via try-catch. – Mitya Jul 31 '12 at 14:10
  • 3
    +1 for wanting to properly try catch all your functions. Though, I think you are going to have to edit them all manually. – Limey Jul 31 '12 at 14:11
  • 1
    Try-catch is very expensive. Are you sure you want to do this? What is the requirement? – The Internet Jul 31 '12 at 14:12
  • 1
    You could just as well wrap the entry point into the code with a single try catch... The error will bubble up. This won't work necessarily for attach event handlers and so forth, but any functions nested within the first will simply bubble the error up. – KingOfHypocrites Jul 31 '12 at 14:21
  • @KingOfHypocrites, your answer sounds intriguing but I'm not getting it completely. What do you mean by 'wrap the entry point into the code with a single try-catch'? Do you mean I should put all my javascript inside one try{}? – Wytze Aug 13 '12 at 06:58

6 Answers6

27

This isn't simple since there is no way to find all JavaScript function defined everywhere. For example, any such approach would probably miss callback functions which are defined at runtime.

You also probably don't want to wrap all functions because that would include browser functions and functions from JavaScript libraries that you certainly don't want to wrap.

A much better approach is probably to define a function which wraps another function:

var tcWrapper = function(f) {
    return function() {
        try {
            f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}

Now you can use this function to decorate anything that you want. Wrapping will become more simple if you use name spaces:

var NS = { f: function() {  } }

Just put all functions to wrap in a special namespace and then iterate over the namespace:

$.each( NS, function(i,n) {
    var p = NS[i];
    if( typeof p === 'function' ) {
        NS[i] = tcWrapper(p);
    }
} );
Edwin Thomas
  • 466
  • 4
  • 16
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • Not all functions will want the same 'this'. You might want to be conscious about which 'this' you're sending in. Eg, event handlers sometimes get the this being the event target, or object methods. You can however loop over object properties and replace them by a getter (with Object.defineProperty) which wraps them in an inline function that could both fix their this to a value that can't be tampered with from the outside, as well as wrapping them in a try catch block. –  Jan 02 '15 at 00:59
  • 2
    @nus: I don't see your point. I just pass `this` on but maybe I'm doing it wrong? Maybe your information is too complex for a comment. How about showing us code and the reasons in another answer? – Aaron Digulla Jan 05 '15 at 09:43
  • I'm sorry, my mistake I failed to see apply only gets run when the returned function is already attached to NS. I somehow thought it was being called in the context of tcpWrapper, in which case it would have fixed the `this` from tcpWrapper on every function. –  Jan 06 '15 at 08:04
11

I don't have enough reputation to comment on the accepted answer.

I added a return before the f.apply to pass up the return value as well.

var tcWrapper = function(f) {
    return function() {
        try {
            return f.apply(this, arguments);
        } catch(e) {
            customErrorHandler(e)
        }
    }
}
Fullchee Zhang
  • 489
  • 5
  • 13
5

The given answers already work well, just wanted to share a new one using closures.

Define wrapper

const tryCatchWrapper = (executable) => async (...args) => {
    try {
        const result = await executable(...args);
        return result;
    } catch (error) {
        // use any custom handler here
       error.message = `[${executable.name}] - ${error.message}`;
       error.data = {...error.data, input_args: args}
       throw error;
    }
}

Your function without a try-catch wrapper

const myFunction = async (x, y) => {
    const sum = x + y;
    if (sum > 10) throw new Error(`sum > 10 custom error`)
    return sum;
}

How to use it

try {
    const wrapperFunction = trycatchWrapper3(myFunction2);
    const output = await wrapperFunction(2, 93)
    console.log(output)
} catch (error) {
    console.error(error)
}

END

Jassi
  • 619
  • 7
  • 12
1

Okay, I seem to have found it here: http://www.nczonline.net/blog/2009/04/28/javascript-error-handling-anti-pattern/

Basically, all functions are replaced by a try-catch wrapper with the original function in the try part.

Wytze
  • 7,844
  • 8
  • 49
  • 62
  • 7
    The title of the blog post says it's an anti-pattern, i.e. possible but inadvisable. Up to you, but I really don't think this is a good idea. – Mitya Jul 31 '12 at 14:12
  • 4
    The blog posts discusses anti-patterns but also shows solutions. – Aaron Digulla Aug 02 '12 at 09:37
1

I needed to go fortify some code, so I wrote a function called fortify and put it in an NPM module. It's a work in progress, but it should help.

https://github.com/infinitered/over-armour

Bonus: it works with async functions. Feedback welcome

Gant Laborde
  • 6,484
  • 1
  • 21
  • 30
-3

I wonder (this is pure speculation, so not sure if this would work) you could do something like this:

function callTryCatch(functionSignature) {
    try {
        eval(functionSignature);
    } catch (e) {
        customErrorHandler(e);
    }
}

function entryPoint() {
    callTryCatch(function() {
        // do function logic
    });
}

Again, this is pure speculation and I haven't tested but if it's even possible I think the key lies in the eval statement.

Paul Aldred-Bann
  • 5,840
  • 4
  • 36
  • 55