Is it possible to catch exceptions thrown in a JavaScript async callback?

JavascriptGoogle MapsExceptionCallback

Javascript Problem Overview


Is there a way to catch exceptions in JavaScript callbacks? Is it even possible?

Uncaught Error: Invalid value for property <address>

Here is the jsfiddle: http://jsfiddle.net/kjy112/yQhhy/

try {
    // this will cause an exception in google.maps.Geocoder().geocode() 
    // since it expects a string.
    var zipcode = 30045; 
    var map = new google.maps.Map(document.getElementById('map_canvas'), {
        zoom: 5,
        center: new google.maps.LatLng(35.137879, -82.836914),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });
    // exception in callback:
    var geo = new google.maps.Geocoder().geocode({ 'address': zipcode }, 
       function(geoResult, geoStatus) {
          if (geoStatus != google.maps.GeocoderStatus.OK) console.log(geoStatus);
       }
    );
} catch (e) {
    if(e instanceof TypeError)
       alert('TypeError');
    else
       alert(e);
}​

Javascript Solutions


Solution 1 - Javascript

The reason it won't catch anything in your example is because once the geocode() callback is called, the try/catch block is over. Therefore the geocode() callback is executed outside the scope of the try block and thus not catchable by it.

As far as I know, it is not possible to catch exceptions thrown in JavaScript callbacks (at least, not in any straightforward manner).

Solution 2 - Javascript

Yes, you can override the default behaviour of window.onerror:

window.onerror = function(message, file, lineNumber) {
  // all errors will be caught here
  // you can use `message` to make sure it's the error you're looking for
  // returning true overrides the default window behaviour
  return true; 
};

Solution 3 - Javascript

You can indeed catch exceptions that fire within a JavaScript callback function.

The key is to set up the try/catch block within the callback code, as any try/catch blocks outside the callback code will have already exited by the time the callback code is executed. So while your try/catch block above won't be able to catch any exceptions that get thrown when the callback function is called, you can still do something like this:

// this will cause an exception ing google.maps.Geocoder().geocode() 
// since it expects a string.
var zipcode = 30045; 
var map = new google.maps.Map(document.getElementById('map_canvas'), {
    zoom: 5,
    center: new google.maps.LatLng(35.137879, -82.836914),
    mapTypeId: google.maps.MapTypeId.ROADMAP
});
// exception in callback:
var geo = new google.maps.Geocoder().geocode({ 'address': zipcode }, 
   function(geoResult, geoStatus) {
      try {
          if (geoStatus != google.maps.GeocoderStatus.OK) console.log(geoStatus);
      } catch(e){
          alert("Callback Exception caught!");
      }
   }
);

and you'll be able to capture the exception when it is thrown. I wasn't 100% sure whether that would be the case or not, so I wrote some test code to verify. The exception is captured as expected on Chrome 19.0.1055.1 dev.

Solution 4 - Javascript

I have detected the error by monkey patching the console logs.

if(window.console && console.error){
    var old = console.error;
    console.error = function(){
        if(arguments[0].indexOf('Google Maps API error')!=-1){
            alert('Bad Google API Key '+ arguments[0]);
        }
        Array.prototype.unshift.call(arguments);
        
        old.apply(this, arguments);
    }
}

Solution 5 - Javascript

If you can use Promises and async/await, it can be solved as shown in sample code below:

async function geocode(zipcode) {

  return new Promise((resolve, reject) => {

    const g = new google.maps.Geocoder().geocode({ 'address': zipcode },  function(geoResult, geoStatus) {
      if (geoStatus != google.maps.GeocoderStatus.OK) {
        reject(new Error("Callback Exception caught"));
      } else {
        resolve(g);
      };

    });
  });
}

try {
  // ...

  // g will be an instance of new google.maps.Geocoder().geocode..
  // you can resolve with desired variables
  const g = await geocode(zipcode);

  // ...
} catch( e ) {
  console.log(e);
}

Solution 6 - Javascript

Here's my approach:

// the purpose of this wrapper is to ensure that any
// uncaught exceptions after a setTimeout still get caught
function callbackWrapper(func) {
    return function() {
        try {
            func();
        } catch (err) {
            // callback will reach here :)
            // do appropriate error handling
            console.log("error");
        }
    }
}

try {
    setTimeout(callbackWrapper(function() {throw "ERROR";}), 1000);
} catch (err) {
    // callback will never reach here :(
}

Solution 7 - Javascript

According to all answers, try/catch + callback is set on a different context but then - how would you explain this code try/catch working?

function doSomeAsynchronousOperation(cb) {
  cb(3);
}

function myApiFunc() {
  /*
   * This pattern does NOT work!
   */
  try {
    doSomeAsynchronousOperation((err) => {
      if (err) {
        console.log('got here');
        throw err;
      }
      
    });
  } catch (ex) {
    console.log(ex);
  }
}

myApiFunc();

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionanewbView Question on Stackoverflow
Solution 1 - JavascriptDaniel VassalloView Answer on Stackoverflow
Solution 2 - JavascriptJivingsView Answer on Stackoverflow
Solution 3 - JavascriptJustin HoukView Answer on Stackoverflow
Solution 4 - JavascriptKaran BhandariView Answer on Stackoverflow
Solution 5 - JavascriptroxxypoxxyView Answer on Stackoverflow
Solution 6 - JavascriptMartin KonecnyView Answer on Stackoverflow
Solution 7 - JavascriptUri AbramsonView Answer on Stackoverflow