简体   繁体   中英

Javascript error stops code execution

Whenever an error occurs inside an event handler, it stops code execution entirely so the second event callback isn't called.

For example:

$(function() {
    window.thisDoesntExist();
}
$(function() {
    //Do something unharmful and unrelated to the first event
}

You can easily solve the problem in this (simplified) example by adding try/catch in both anonymous functions, but in reality these functions often add several other event handlers which in turn would require try/catch. I end up with very repetitive code stuffed with try/catch blocks.

My projects has a modular design where each feature is in a different JS (and gets concatenated during a build process). I'm looking for a more generic way to handle errors inside each feature so that the error doesn't stop code execution of the other features.

I already tried following solutions: - window.onerror (even if you return true in this function, code execution is stopped) - $(window).error() => deprecated and code execution stops

You could create a helper function to prevent duplication of the same boilerplate code.

function tryFunction(f, onerror) {
    try {
        if (typeof f == 'function') {
            return f();
        }
    } catch (e) {
        return onerror(e);
    }
}

$(function() {
    var result = tryFunction(window.thisDoesNotExist, function (error) {
        alert('Whoops: ' + error);
    });
});

I created a little demonstration . It's slightly different but the same idea.

You can simply call if (typeof myFunction == 'function') before calling myFunction()

And optionally wrap it in a generic function like said by Bart to have the choice to log an error in the console if your function does not exists.

If your webapp is huge with many interaction and JS, too many try catch could alter the global performance of your application.

I would try something like this with a wrapper which will handle the try catch for you (see below, or this jsfiddle : http://jsfiddle.net/TVfCj/2/ )

From the way I'm (not, and not really) handling the this and the arguments, I guess it's obvious I'm beginning with js. But I hope you get the idea, and it is correct/useful.

  var wrapper = {
      wrap: function wrap(f) {
          return function (args) {
              try {
                  f.apply(null, args);
              } catch (ex){
                  console.log(f.name+" crashed with args "+args);
              };
          };
      }
  };

  var f1 = function f1Crashes(arg) {
      return window.thisDoesntExist();
  };
  var f2 = function f2Crashes(arg) {
      return window.thisDoesntExist();
  };

  var f3 = function f3MustNotCrash(arg) {
      wrapper.wrap(f1)(arg);
      wrapper.wrap(f2)(arg);
  }

  f3('myarg');

The try - catch pattern you mention attempting in your question is the correct way - you want try - catch blocks, not a way to silently truck through module errors (in general always be extremely careful handling exceptions globally and continuing, that way lies data corruption bugs you only find 6 months later).

Your real problem is this:

... in reality these functions often add several other event handlers which in turn would require try/catch. I end up with very repetitive code stuffed with try/catch blocks.

The fix for that is Promise . This is a new structure, native in most browsers but easily shimmed in the slow ones (ahem, IE), that gives you a standard way of managing both the event callback and the exception from the event.

With a Promise your code makes a promise to always do something: either resolve/succeed or reject/fail.

function moduleA() {
    return new Promise(function (resolve, reject)
    {
        try{
            var result = window.thisDoesntExist();
            resolve(resolve); // Success!
        }
        catch(err){
            reject(err); // Fail!
        }
    });
}

This is better because rather than nest try - catch blocks in each callback you can instead chain promises:

moduleA().
    then(moduleB).
    then(moduleC).
    catch(errorHandler); // Catch any error from A, B, or C

You can also handle an error and continue:

moduleA().
    catch(continuableErrorHandler). // Catch any error from A
    then(moduleB).
    then(moduleC).
    catch(errorHandler); // Catch any error from B or C

You'll still need lots of try - catch blocks in callbacks, but anything that has been wrapped in a Promise can be treated in the same modular way.

Coming next in JS is async and await , but you can use them now with a transpiler. These use promises to make code that is much easier to read, and most importantly (for you) have a single try - catch at the top that gathers exceptions from the entire Promise chain.

This answer is already too long, but I've blogged about that in more detail .

TL;DR: If your problem is "very repetitive [event callback] code stuffed with try/catch blocks" try using Promise instead.

I found a solution. When using setTimeout, the code is executed in a seperate thread, therefor it won't break any other parts of the webpage.

$(function() {
    setTimeout(function() {
    window.thisDoesntExist();
    }, 0);
});
$(function() {
    setTimeout(function() {
        //Do something unharmful and unrelated to the first event
        alert("This passes")
    }, 0);
});

In this example, the second function is run, even when the first one throws an error. Here's a working example: http://jsfiddle.net/mathieumaes/uaEsy/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM