Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel JSONP requests in jQuery do not trigger multiple "callback events"?

I am experiencing an issue in jQuery when I do multiple jsonp requests, all with the same jsonpCallback function. It seems that only for the one of those the callback function is triggered. Are JSONP requests somehow overwriting each other?

Below an example of doing 2 jsonp request to github, and even though both firebug shows that both of them return, the callback function getName is only called for one of them:

function getName(response){
    alert(response.data.name);
}

function userinfo(username){
    $.ajax({
        url: "https://api.github.com/users/" + username,
        jsonpCallback: 'getName',
        dataType: "jsonp"
    });        
}

users = ["torvalds", "twitter", "jquery"]
for(var i = 0; i < users.length; i++){
  userinfo(users[i]);
}
like image 621
Jeroen Ooms Avatar asked Apr 23 '12 23:04

Jeroen Ooms


3 Answers

Your request fired only once because of how jsonp works.

Jsonp means adding a script tag to the page from an outside domain to get around Cross-Site Scripting protections built into modern browsers (and now IE6 and 7 as of April 2011). In order to have that script interact with the rest of the script on the page, the script being loaded in needs to call a function on the page. That function has to exist in the global namespace, meaning there can only be one function by that name. In other words, without JQuery a single jsonp request would look like this:

<script>
function loadJson(json) {
    // Read the json
}
</script>
<script src="//outsidedomain.com/something.js"></script>

Where something.js would look like this:

loadJson({name:'Joe'})

something.js in this case has a hard-coded callback to load the JSON it carries, and the page has a hard-coded loadJson function waiting for scripts like this one to load and call it.

Now suppose you want to be able to load json from multiple sources and tell when each finishes, or even load JSON from the same source multiple times, and be able to tell when each call finishes - even if one call is delayed so long it completes after a later call. This hard-coded approach isn't going to work anymore, for 2 reasons:

  1. Every load of something.js calls the same loadJson() callback - you have no way of knowing which request goes with which reply.

  2. Caching - once you load something.js once, the browser isn't going to ask the server for it again - it's going to just bring it back in from the cache, ruining your plan.

You can resolve both of these by telling the server to wrap the JSON differently each time, and the simple way is to pass that information in a querystring parameter like ?callback=loadJson12345. It's as though your page looked like this:

<script>
function loadJson1(json) {
    // Read the json
}
function loadJson2(json) {
    // Read the json
}
</script>
<script src="//outsidedomain.com/something.js?callback=loadJson1"></script>
<script src="//outsidedomain.com/somethingelse.js?callback=loadJson2"></script>

With JQuery, this is all abstracted for you to look like a normal call to $.ajax, meaning you're expecting the success function to fire. In order to ensure the right success function fires for each jsonp load, JQuery creates a long random callback function name in the global namespace like JQuery1233432432432432, passes that as the callback parameter in the querystring, then waits for the script to load. If everything works properly the script that loads calls the callback function JQuery requested, which in turn fires the success handler from the $.ajax call.

Note that "works properly" requires that the server-side reads the ?callback querystring parameter and includes that in the response, like ?callback=joe -> joe({.... If it's a static file or the server doesn't play this way, you likely need to treat the file as cacheable - see below.

Caching

If you wanted your json to cache, you can get JQuery to do something closer to my first example by setting cache: true and setting the jsonpCallback property to a string that is hardcoded into the cacheable json file. For example this static json:

loadJoe({name:'Joe'})

Could be loaded and cached in JQuery like so:

$.ajax({
    url: '//outsidedomain.com/loadjoe.js',
    dataType: 'jsonp',
    cache: true,
    jsonpCallback: 'loadJoe',
    success: function(json) { ... }
});
like image 119
Chris Moschini Avatar answered Nov 19 '22 02:11

Chris Moschini


Use the success callback instead..

function userinfo(username){
    $.ajax({
        url: "https://api.github.com/users/" + username,
        success: getName,
        dataType: "jsonp"
    });        
}
like image 3
Gabriele Petrioli Avatar answered Nov 19 '22 02:11

Gabriele Petrioli


$(function() {
    function userinfo(username){
      var XHR = $.ajax({
             url: "https://api.github.com/users/" + username,
             dataType: "jsonp"
         }).done(function(data) {
             console.log(data.data.name);
         });
    }

    users = ["torvalds", "twitter", "jquery"];
    for(var i = 0; i < users.length; i++){
       userinfo(users[i]);
    }
});  ​
like image 1
adeneo Avatar answered Nov 19 '22 02:11

adeneo