7

I'm sending out a bunch of getJSON() requests to a remote server (to fetch images), and I'd like to display the responses (images) in the same order in which I send the requests. Problem is, AJAX is asynchronous, so the responses come in whatever order they want - usually all mixed up.

I could queue them or make them synchronous - only sending out one request at a time - but that will severely limit the performance.

So is there a way I can identify which response belongs to which request when the responses come back? I was thinking you could put an "id" variable into the JSON callback parameter (e.g. callback=response03) and then somehow parse that callback function name when the response arrives (thus grabbing the id, "03"). But probably not.

My code is something like this:

// Send off requests for each keyword string
$.each($imageRequests, function() {
    $request = this;
    $url = "http://www.example.com/api?q="+$url;
    $.getJSON($url, function($response) {
        if($response.data.items) {
            $.each($response.data.items, function($i, $data) {
                $imgUrl = $data.url;
                $("#imageList").append($imgUrl);
            });
        }
    });
});

I've tried creating a bunch of new divs to hold the returned images, thinking I could populate the divs with their respective images, but that didn't work either.

// Create new div with unique id using line number
$i = 0;
$.each($lines, function() {
    $newDiv = '<div id="img_'+$i+'"></div>';
    $("#imageList").append($newDiv);
    $i++;
});

// Then do the same as the code above but shove the responses into "#img_$i" using the iterator variable to "keep track" (which didn't work).

I've searched and although there are similar questions about AJAX on here, none are as specific as what I'm looking for.

Thanks.

EDIT - heading to bed just now but I will be back on tomorrow - if you can, please check back. I really appreciate the help. :)

Community
  • 1
  • 1
BigJeffrey
  • 141
  • 2
  • 9
  • So basically, you want the images to appear on the site in the same order in which they were requested? – Kevin Peno Apr 26 '11 at 23:13
  • I don't quite understand your question; you want to load items synchronously (after each other) but "not synchronously"? – Christian Apr 26 '11 at 23:30
  • I'm not sure why you have two '$.each'. Do you have multiple requests that return multiple images? – morgar Apr 26 '11 at 23:45
  • @Kevin - Yes. That's it exactly. @Christian - I want to make a bunch of requests all at the same time, receive the responses in any order, but sort them / process them (insert them into the page) in the same order in which I requested them. @morgar - The reason I tried adding a second "each" loop was to first set up a series of "pre-made" divs into which I would later insert each image. I figured if the divs were already on the page, the images could be inserted into them in order. I was not thinking clearly, and I was wrong. – BigJeffrey Apr 27 '11 at 00:21
  • @Christian - I figured I should expand my explanation more. I don't need to actually get the responses in any particular order. They can come back in whatever order they want to. But as they do, I want to be able to sort them into the correct order. So if responses #3, #1, and #2 come back in that order, #3 will appear on the page first, then #1 will appear before it, then finally #2 (lastly) will be received but will be inserted onto the page in between #1 and #3. See? It's about displaying them in order, not necessarily receiving/loading them in order. Thanks. – BigJeffrey Apr 27 '11 at 00:30

4 Answers4

6

You're almost there; just need to create the div's in a way that the Ajax callback function can reference the corresponding div, i.e. using a closure. Something like this:

$.each(urls, function(i, url) {
    var div = $('<div />');
    list.append(div); // list is the container element that holds the images

    $.getJSON(url, function(data) {
        // assuming data is an image url - adjust accordingly
        div.append('<img src="' + data + '" />');
    });
});
Community
  • 1
  • 1
Jeffery To
  • 11,836
  • 1
  • 27
  • 42
  • Yes, this worked. I'm actually still not clear exactly how it worked, but I will read up on closures. Thank you, and indeed thanks to everyone else too. – BigJeffrey Apr 27 '11 at 21:39
  • @BigJeffrey If I understand this approach correctly, it first appends the empty div elements in the correct order. The order in which the ajax calls are executed do not matter anymore, as all they do is fill the already injected div. Kind of neat. – k0pernikus Jul 10 '13 at 06:32
0

Rather than appending the ID, how about a datestamp instead? This way you can really sort by "when" the request was made. Your temp div idea can also work nicely if you use the TinySort plugin.

Create a timestamp variable somewhere else..

var stamp = new Date();

Then append the milliseconds to each request.

$.each($imageRequests, function() {
    $request = this;
    $url = "http://www.example.com/api?q="+$url+"&stamp="+stamp.getMilliseconds();
    $.getJSON($url, function($response) {
        if($response.data.items) {
            $.each($response.data.items, function($i, $data) {
                $url = '<img alt="'+ $data.stamp +'" src="'+ $data.url +'" />';
                $("#tempdiv").append($url);

                // After appending, you can sort them
                $("#tempdiv").tsort("img",{order:"desc",attr:"alt"});
            });
        }
    });
});
eksith
  • 156
  • 4
  • Oh. I didn't realise you could pass your own parameters in the url and have them returned. That would surely be the exact solution I've been looking for? Does this work for all JSON GET requests as standard? I presumed that any parameters that were not part of the RESTful web service would have been stripped out by the service. Thank you for your reply. – BigJeffrey Apr 27 '11 at 00:26
  • Addendum... But whichever remote server you're calling still needs to return "stamp" data along with "url" data back from the server so that needs to be added to the Json response. Do you have access to the remote server? Can you control the resulting Json response in any way? – eksith Apr 27 '11 at 01:25
  • No, sadly, I can't control the response. It's a public API. That's why I wondered about how you could pass your own values to it and have them returned. That's not possible, right? – BigJeffrey Apr 28 '11 at 00:12
0

each() callback in jQ has a parameter - index of the element or number of iteration. So you can create array of s upfront and upon arrival to set/modify div at that index:

var divs = $("div.your-class");
$.each($imageRequests, function(irIndex) {
    $.getJSON(url, function(response) {
     // change element at irIndex using the response.
     divs[irIndex].someUpdate(response); 

    });
});
c-smile
  • 26,734
  • 7
  • 59
  • 86
  • But will the calls be made in a sequential order (e.g. 0-9)? Would they not still be made asynchronously? (Forgive me - I can't test right now as I'm about to go to bed but will do so tomorrow. Thank you! :)) – BigJeffrey Apr 27 '11 at 00:25
  • Preparation step: `$("#imageList").append("
    ");` as many $imageRequests you have. So you will prepare N empty elements in order. Then run that $.each() above. On data arrival your callback function will use irIndex'es to set data to proper elements. Order of arrival does not matter. Note `function(response)` creates a closure here - instance of the function + outer frame that includes irIndex.
    – c-smile Apr 27 '11 at 03:21
0

It's really helpful to have proper CPS when doing things like this. With JooseX-CPS you can use its AND construct to parallelize operations, guaranteeing the correct order of returned values. And here's a tutorial (check Show me the paralell).

Adam Bergmark
  • 7,316
  • 3
  • 20
  • 23