Search code examples
javascriptajaxjsonpprototypejs

Dynamic JSONP approach


I was wondering whether my approach to JSONP is correct and if not, what I can change.

My standard JSONP approach:

Server

Standard ?jsonp=callback parameter

Client

  1. Call the function (example: jsonp(url, callback)) *1
  2. Create a <script> tag *2
  3. Execute the script
  4. Remove the script tag

Code
Note: I just wrote all of these in the stackoverflow editor, code is untested
1:

jsonp("http://example.com/sampleAPI", function(result) {  
    // Do something  
});

2:

var callbackID = 0;
var jsonp = function(url, callback) {
    var callbackName = 'callback' + callbackID;
    // put the callback in the URL
    callbackID++;
    if (url.indexOf('?') != -1) {
        url += '&jsonp=' + callbackName;
    } else {
        url += '?jsonp=' + callbackName;
    }

    // create the callback
    window[callbackName] = function(result) {
        $('jsonpScriptElement' + callbackID).remove();
        callback(result);
        window.splice(callbackName);
    }

    // Create the actual element (do this as the last thing because of caching)
    // Assuming prototype.js usage
    $$('body')[0].insert(new Element('script', {'href': url, 'id': 'jsonpScriptElement' + callbackID}));
}

Solution

  • The only problem I see with it this is this part:

    window[callbackName] = function(result) {
        $('jsonpScriptElement' + callbackID).remove();
        callback(result);
        window.splice(callbackName);
    }
    

    You are making a new function for every call you make. Try to avoid this by making a generic function that is still safe to use asynchronously. Shouldn't be too hard to come up with something. You could for instance pass the sequence number along to the JSONp call and back to the callback so you can keep the different requests apart.

    And yes, JSONp is really this easy :P

    Additionally you can change your JSONp function to this:

    var jsonp = (function() {
        var callbackID = 0;
        return function (url, callback) {
             //.. function body ..
        }
    }());
    

    So you don't have to declare callbackID in the global scope