0

I have some 'ajax' calls (really sjax I guess you could call it) and I'm trying to make them render on the page one at a time, but they aren't. They all render at the end. How can I space them out?

function getNames() {
    var names = $('#thenames').val();
    alert(names);
    var splitnames = names.split(',');
    for(var i = 0; i < splitnames.length; i++) {
    var name = splitnames[i];

    $.ajax({
        type: 'GET',
        url: '/acert/secure/people/namesservice/getnamesajax.jsp',
        data: { usernames: name},
        success: function(data) { $('#results').html($('#results').html() + data);},
        async: false });
        }
    }
}

I can't risk them coming back in the wrong order so I need them to be synchronous. I put them into a for-loop, so the for-loop should give the browser a chance to render between calls, but I can't seem to make it.

Any ideas on what I'm doing wrong?

If I add an alertbox in the success function it works, but I don't want to have to babysit the operation, I just want to monitor its progress now and again.

corsiKa
  • 81,495
  • 25
  • 153
  • 204
  • Why not make the next AJAX call in the onsuccess callback function? – zz3599 Feb 28 '13 at 16:19
  • The for loop will not allow time for the browser render, synchronous requests will also not allow the browser to render. There is a way to do this without `async: false` while still preserving the order. – Kevin B Feb 28 '13 at 16:20

4 Answers4

2

async: false blocks the browser. It completely locks up everything, including repaints to the DOM.

I strongly strongly recommend you don't use async: false. It is extremely bad.

You might be able to use setTimeout in-between the calls, but they don't guarantee the browser will trigger a repaint.

If you set async: true you will not have this problem, but you will likely have to change your code to properly deal with asynchronous behaviour.


async false is so bad jQuery decided to remove it from the API.

Community
  • 1
  • 1
Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • I see. So I made the index a parameter to `getNames`, and am calling `getNames` in my success function. It's async by name, but it's still logically synchronous since one leads to the next. – corsiKa Feb 28 '13 at 16:25
  • Yes, that is the correct way to do it. Set up some sort of asynchronous iterator. – Halcyon Feb 28 '13 at 16:26
1

Do not use async: false.

The code below will run all ajax requests as fast as possible, then append the content to #results in the correct order. DO NOT include async: false if you are using the code below.

var defArr = [];

for(var i = 0; i < splitnames.length; i++) {
    defArr.push( $.ajax({...}) );
}

$.when.apply($,defArr).done(function(){
    var $results = $("#results");
    $results.empty();
    for (var i = 0; i < arguments.length; i++) {
        $results.append(arguments[i][0]);
    }
});
Kevin B
  • 94,570
  • 16
  • 163
  • 180
  • I've never seen `when` `apply` or `done`. Wow. Does that `append(newhtml)` method do the same as `$('#id').html($('#id').html() + newhtml)`? – corsiKa Feb 28 '13 at 16:27
  • @corsiKa Yes, it will just add to the existing content instead of re-parsing and re-creating the previous content. Fixed a typo. – Kevin B Feb 28 '13 at 16:27
  • For more information about deferred objects (which is what `.when` and `.done` come from) you can start here: http://learn.jquery.com/code-organization/deferreds/ as far as `.apply`, it just applies the passed in array as arguments the the function it was called on. the first argument is the context, aka `this` but isn't important to `.when`, so i just passed `$`. – Kevin B Feb 28 '13 at 16:30
  • Well that just completely made my day. Not even lying. I've always been a backend guy, and (as you can see) I really struggle on the front end stuff. But it turns out business users don't care about algorithms and databases, they care about what they can see. I'm going to look up each of the methods used here, because I'm assuming that with the speed you came up with this answer, this is pretty basic stuff for anyone who calls themselves a 'web dev'... – corsiKa Feb 28 '13 at 16:30
  • @corsiKa You probably wont find a whole lot about using `$.when.apply`, I made a blog entry a year or so ago before i stopped posting in my blog, you can find a link to it in my profile. – Kevin B Feb 28 '13 at 16:33
0

assuming you know how many calls you make (or can include that as a parameter to the return result) you can simply fire the calls asynchronously, and make them elements in an array on your success callback. When the array gets to the expected size, just render them in sequence.

Nick Andriopoulos
  • 10,313
  • 6
  • 32
  • 56
0

For one, you seem to have an extra curly brace there.

But more to the issue at hand, if you just want to monitor the progress would it work for you to use setTimeout?

-- update --

I think I get what you're trying to do. And if I'm not mistaken, you could refactor a little, and then use a closure and an object with the names as the keys. Something like this:

function getNames()
{
    var names = $('#thenames').val();
    var splitnames = names.split(',');
    var myData = {};


    for(var i = 0; i < splitnames.length; i++) 
    {
        (function(name) 
            { return function(){
            $.ajax({
                type: 'GET',
                url: '/acert/secure/people/namesservice/getnamesajax.jsp',
                data: { usernames: name},
                success: function(data) { myData[name] = data; updateNames(); }
            });

        })( splitnames[i] )
    }
}

What this basically does is that it sets up a bunch of ajax calls right away, that weird bit in the middle with the (function(){})() makes sure you don't end up fetching the last value that name gets set to when the loop finishes. Everything gets saved to myData, I figured once everyone is loaded you could check to see if all the names you have in splitnames are in myData with the updateNames function. Something like

var count = 0; 
for ( var i = 0; i < splitnames.length; i++ ) 
{
    count += myData[splitnames[i]] != null ? 1 : 0; 
}

if (count == splitnames.length)
{
   // write the names to the screen
}

Does that make sense?

But, to be honest, the best approach would probably be to change the getnamesajax.jsp so that it accepts all the names, and then gives you back the info you need in the order you need. If that was an option, that would be best since you would only need to make one ajax call.

fet
  • 614
  • 5
  • 12