Monday, June 15th, 2009

Bing API does callback checking for JSON-P

Category: JavaScript, JSON, Library

<p>I just looked through the API of Microsoft’s new Bing search and found an interesting step in protecting code from throwing errors.

When you provide a JSON output for developers it does make sense to also allow for a callback parameter. That way your code can be used in script nodes without having to use any backend at all. Commonly this is called JSON-P and has been covered here in the long long ago. One of the issues with JSON-P is that when the callback method is not defined it throws an error.

The Bing API is the first instance where I have seen that they worked around that as the output is this:

javascript
< view plain text >
  1. if(typeof callback == 'function') callback(
  2. {
  3.   "SearchResponse":
  4.   {
  5.     "Version":"2.2",
  6.     "Query":
  7.       {
  8.         "SearchTerms":"a hard day's night"
  9.       },
  10.     "Translation":
  11.       {
  12.         "Results":
  13.           [
  14.             {"TranslatedTerm":"einem harten Tag-Nacht "}
  15.           ]
  16.       }
  17.     }
  18. } /* pageview_candidate */);

I have no clue what the /* pageview_candidate */ is about and frown upon omitting the {} of the if statement, but I must say I do like this. One issue is that while end users don’t get annoyed with errors, developers don’t have a clue what happened either as the error is silent. One proposal would be to use a console.log() when there is an error:

javascript
< view plain text >
  1. if(typeof callback === 'function') {
  2.   callback(... data ... );
  3. } else {
  4.   if (typeof console!=='undefined' &&
  5.    typeof console.log !== 'undefined'){
  6.     console.log('Error: Callback method not defined');
  7.   }
  8. }

More details about other ideas to improve this are here.

Related Content:

19 Comments »

Comments feed TrackBack URI

Don’t bother with “console” – just throw a native error:

throw new Error(‘Callback method is undefined or a not a function’);

I don’t think Bing should be second-guessing API users by placing any checks before the data. Can I just have the data!??

Comment by JimmyP22 — June 15, 2009

JimmyP22 this could be another parameter.

Comment by ChrisHeilmann — June 15, 2009

Google maps has done this for a while, albeit without type checking:

callback && callback({...});

Comment by kissmyawesome — June 15, 2009

RESTEasy from JBoss does the same type of test as Google Maps when serving JSON-P

Comment by jvelo — June 15, 2009

This is a waste of bytes, the default error that is thrown when the callback is not available (ReferenceError: callback is not defined) is certainly adequate, why write a new one?

Comment by kriszyp — June 15, 2009

and frown upon omitting the {} of the if statement

You frown upon using standard syntax? :/

Comment by Darkimmortal — June 15, 2009

You have an error in your sample code: } else {(typeof console!=='undefined' && should probably be } else if(typeof console!=='undefined' &&

- – -

DarkImmortal,

Yes, sometimes the best programming practices means omitting some standard syntax possibilities. While leaving out the curly braces from conditionals works it encourages coding errors. The way it’s formatted in the example, it’s easy to miss the if statement entirely. With other formatting, it could be easy to not see what is affected by the if statement, to accidentally change what is affected by the if statement, or to mistakenly add code that looks like it should be affected by the if statement but is not.

Use curly braces. It’s only a difference of 0.002 KiB, greatly enhances readability, and could be programatically stripped for output if you really need to recover every byte possible.

Comment by eyelidlessness — June 15, 2009

@Darkimmortal. I also frown at that. It’s an accident waiting to happen.

Comment by Nosredna — June 15, 2009

I can’t speak to the originality of this “trick”, but it’s a nice little addition for JSON-P services. I think I’d prefer to wrap the callback call in try…catch and pop an alert(), and then give the caller of the JSON-P service the ability to turn this on or off (because you wouldn’t want to show your users an alert() with some cryptic JS exception info in production for example, but in dev it’d be very useful).

That being said, it doesn’t do anything of course to address the single biggest problem with JSON-P, and that’s not knowing if a response timed out or otherwise failed. I.e., make a request that you get a 404 or 500 for and you never know it. If someone has a clever way of dealing with that I would absolutely LOVE to hear it :)

Comment by fzammetti — June 15, 2009

Not only the not having braces for a codeblock after the if(), the typeof can always always f’scking ALWAYS be checked with a === or !==

Comment by blinkingmarquee — June 15, 2009

edit – Okay, they only have that in the first sample.

to Darkimmortal — good rundown.

Comment by blinkingmarquee — June 15, 2009

@kriszyp, The default error is not sufficient. Throw a ReferenceError and your execution halts. Then you may have left the page half finished and fully broken. If the API checks that your callback is not a callable function then it can throw its own error and execution continues.

@blinkingmarquee, No need to use strict comparison with typeof. The typeof operator always returns a string. So something like typeof “abc” == “string” is sufficient.

Comment by bayareacoder — June 15, 2009

@bayareacoder: No, throwing your own error halts execution in the same way; it halts execution of the JSONP script, which doesn’t affect the execution of the caller at all.

Comment by kriszyp — June 15, 2009

@fzammetti

What about something like this – issues the request along with a timeout that cancels the request if the callback isn’t invoked after a certain period (10 secs in this example). Not tested but you get the idea:

var requestManager={
timeout:10,
complete:[],
add:function(url, callback, onError){
var timer;
var func=function(json){
clearTimeout(timer);
callback(json);
};
var id=this.complete.push(func)-1;
timer=setTimeout(function(){
requestManager.complete[id]=function(){};
if (onError) onError();
}, requestManager.timeout*1000);

requestManager.go(url,id);
},
go:function(url, id){
var src=url+"&callback=requestManager.complete["+id+"]";
// create / add to a script element to invoke request
}
};

requestManager.add("http://json.com?a=b", function(json){
// got me some JSON
},function(){
// timed out or otherwise failed
});

Comment by kissmyawesome — June 16, 2009

Hmm, totally screwed the formatting. Ajaxian, for the love of god give us comment previews.

Comment by kissmyawesome — June 16, 2009

@kissmyawesome of course you can simulate a timeout (YUI get() is full of awesome for that) but that doesn’t tell you if there was a timeout. I guess what @fzammetti wants s a proper transport of HTTP codes over JSON-P.

Comment by Chris Heilmann — June 16, 2009

@chris – you’re right, I was just using that as a simple workaround. There’s obviously no real way to do it if / until JSONRequest is implemented.

Comment by kissmyawesome — June 16, 2009

@chris: Yeah, I came to a very similar solution in my head while driving home from work last night :) Kiss is right of course, getting at HTTP error codes doesn’t seem possible, but knowing the request has timed out I think is a nice step up.

Comment by fzammetti — June 16, 2009

To “bing” this conversation full-circle (oh, I’m so very clever- LOL): http://www.zammetti.com/blog

Comment by fzammetti — June 17, 2009

Leave a comment

You must be logged in to post a comment.