Eric Simonton
Eric Simonton

Reputation: 6029

Catch Error from gapi.client.load

I'm using Google App Engine with Java and Google Cloud Endpoints. In my JavaScript front end, I'm using this code to handle initialization, as recommended:

var apisToLoad = 2;
var url = '//' + $window.location.host + '/_ah/api';
gapi.client.load('sd', 'v1', handleLoad, url);
gapi.client.load('oauth2', 'v2', handleLoad);
function handleLoad() {
    // this only executes once,
    if (--apisToLoad === 0) {
        // so this is not executed
    }
}

How can I detect and handle when gapi.client.load fails? Currently I am getting an error printed to the JavaScript console that says: Could not fetch URL: https://webapis-discovery.appspot.com/_ah/api/static/proxy.html). Maybe that's my fault, or maybe it's a temporary problem on Google's end - right now that is not my concern. I'm trying to take advantage of this opportunity to handle such errors well on the client side.

So - how can I handle it? handleLoad is not executed for the call that errs, gapi.client.load does not seem to have a separate error callback (see the documentation), it does not actually throw the error (only prints it to the console), and it does not return anything. What am I missing? My only idea so far is to set a timeout and assume there was an error if initialization doesn't complete after X seconds, but that is obviously less than ideal.

Edit:

This problem came up again, this time with the message ERR_CONNECTION_TIMED_OUT when trying to load the oauth stuff (which is definitely out of my control). Again, I am not trying to fix the error, it just confirms that it is worth detecting and handling gracefully.

Upvotes: 6

Views: 3917

Answers (4)

Niesteszeck
Niesteszeck

Reputation: 31

I use a setTimeout to manually trigger error if the api hasn't loaded yet:

console.log(TAG + 'api loading...');
let timer = setTimeout(() => {
  // Handle error
  reject('timeout');
  console.error(TAG + 'api loading error: timeout');
}, 1000); // time till timeout
let callback = () => {
  clearTimeout(timer);
  // api has loaded, continue your work
  console.log(TAG + 'api loaded');
  resolve(gapi.client.apiName);
};
gapi.client.load('apiName', 'v1', callback, apiRootUrl);

Upvotes: 0

chaduhduh
chaduhduh

Reputation: 80

I know this is old but I came across this randomly. You can easily test for a fail (at least now).

Here is the code:

gapi.client.init({}).then(() => {
     gapi.client.load('some-api', "v1", (err) => { callback(err) }, "https://someapi.appspot.com/_ah/api");
}, err, err);

function callback(loadErr) {
    if (loadErr) { err(loadErr); return; }
    // success code here 
}

function err(err){
     console.log('Error: ', err);
     // fail code here
}

Example

Upvotes: 3

Wladimir Palant
Wladimir Palant

Reputation: 57651

Unfortunately, the documentation is pretty useless here and it's not exactly easy to debug the code in question. What gapi.client.load() apparently does is inserting an <iframe> element for each API. That frame then provides the necessary functionality and allows accessing it via postMessage(). From the look of it, the API doesn't attach a load event listener to that frame and rather relies on the frame itself to indicate that it is ready (this will result in the callback being triggered). So the missing error callback is an inherent issue - the API cannot see a failure because no frame will be there to signal it.

From what I can tell, the best thing you can do is attaching your own load event listener to the document (the event will bubble up from the frames) and checking yourself when they load. Warning: While this might work with the current version of the API, it is not guaranteed to continue working in future as the implementation of that API changes. Currently something like this should work:

var framesToLoad = apisToLoad;
document.addEventListener("load", function(event)
{
  if (event.target.localName == "iframe")
  {
    framesToLoad--;
    if (framesToLoad == 0)
    {
      // Allow any outstanding synchronous actions to execute, just in case
      window.setTimeout(function()
      {
        if (apisToLoad > 0)
          alert("All frames are done but not all APIs loaded - error?");
      }, 0);
    }
  }
}, true);

Just to repeat the warning from above: this code makes lots of assumptions. While these assumptions might stay true for a while with this API, it might also be that Google will change something and this code will stop working. It might even be that Google uses a different approach depending on the browser, I only tested in Firefox.

Upvotes: 2

dave
dave

Reputation: 64657

This is an extremely hacky way of doing it, but you could intercept all console messages, check what is being logged, and if it is the error message you care about it, call another function.

function interceptConsole(){
    var errorMessage = 'Could not fetch URL: https://webapis-discovery.appspot.com/_ah/api/static/proxy.html';
    var console = window.console
    if (!console) return;
    function intercept(method){
        var original = console[method];
        console[method] = function() {
            if (arguments[0] == errorMessage) {
                alert("Error Occured");
            }
            if (original.apply){
                original.apply(console, arguments)
            }
            else {
                //IE
                var message = Array.prototype.slice.apply(arguments).join(' ');
                original(message)
            }
        }
    }
    var methods = ['log', 'warn', 'error']
    for (var i = 0; i < methods.length; i++)
        intercept(methods[i])
}

interceptConsole();
console.log('Could not fetch URL: https://webapis-discovery.appspot.com/_ah/api/static/proxy.html');
//alerts "Error Occured", then logs the message
console.log('Found it');
//just logs "Found It"

An example is here - I log two things, one is the error message, the other is something else. You'll see the first one cause an alert, the second one does not.

http://jsfiddle.net/keG7X/

You probably would have to run the interceptConsole function before including the gapi script as it may make it's own copy of console.

Edit - I use a version of this code myself, but just remembered it's from here, so giving credit where it's due.

Upvotes: 1

Related Questions