1

Ok so I have a do-while loop using jQuery. Basically its point is to check to see that there's a photo in the directory and add it to an array. The directory's photos are all named 'phone01.png', 'phone02.png' etc and they're in a directory called 'slides'.

Here's the code:

var images = [];
var i=0;

    do {
        ++i;
        $.get('./slides/phone0' + i + '.png',function(){
            images[(i-1)] = './slides/phone0' + i + '.png';
        });
    }
    while ( images[(i-1)] == './slides/phone0' + i + '.png' );

So if I use a console at the end, images[] only contains './slides/phone01.png', BUT

i is 1

images[(i-1)] is therefore './slides/phone0' + i + '.png'

Entering ( images[(i-1)] == './slides/phone0' + i + '.png' ) returns true. So the while statement is true, but the loop is not continuing.

Moreover, if I manually run the loop again (iterate i, run the get) the get will succeed and add a new member to the array. So, the do function never runs a second time.

What gives?

Spudley
  • 166,037
  • 39
  • 233
  • 307
DudeGuy
  • 77
  • 1
  • 7
  • Will there only ever be <10 images? (<9, unless you start with `phone00.png`.) – David Thomas Oct 22 '12 at 20:08
  • looks like a DDOS attack to me O_o – jAndy Oct 22 '12 at 20:09
  • `$.get` takes time to download files from the server, `do` doesn't wait for it to complete... – DCoder Oct 22 '12 at 20:10
  • 2
    This is a common issue - and there are actually 2 here. 1) there is only **one** `i` variable 2) the get callback is asynchronous. See http://stackoverflow.com/questions/6425062/passing-functions-to-settimeout-in-a-loop-always-the-last-value and http://stackoverflow.com/questions/10194684/jquery-ajax-get-return-value –  Oct 22 '12 at 20:11
  • What are you trying to do, you don't use the loaded images? – Bergi Oct 22 '12 at 20:12

3 Answers3

3

$.get is an asynchronous request. So your loop will always run once as the image array never gets set as you expect.

Lews Therin
  • 10,907
  • 4
  • 48
  • 72
1

This is a classic async issue.

When you call $.get(), it doesn't go immediately into the function you give it.

What happens is that it sets up a HTTP request, and attaches that function to the request as the function to call when the request succeeds.

The original part of the program then carries on with it's own business, and forgets about the HTTP request and the function it passed to it.

In this case, the original part of the program is the while loop. Once it's given away its function, it carries on and loops around the while loop.

The functions that are created on each iteration of the loop are not fired until (a) the HTTP request has a successful response, and (b) the rest of the Javascript code that is running stops blocking the thread and gives it a chance to run.

This is called Asynchronous code, and is the first 'A' the term 'AJAX'. It's also how pretty much the whole of Javascript's event handling model works, and all the nested functions you see in jQuery. (This may also explain other cases you may have seen where code seemed to run in the wrong sequence).

If you want to do a loop like this with Async code, you need to write it quite differently: a traditional while loop as you've written it will not work.

There are a number of was to do it, but rather than just giving you some code, I suggest doing a bit more digging around how this whole thing works, because if you're going to write Ajax code, you need to understand this stuff.

One hint I will give you though: global variables are generally not all that useful in this sort of program. It's not clear from your code sample whether your variables are global or not, but if they are, you'll definitely want to consider making them local variables and passing them around between functions rather than just expecting to set them in one place and for them to work in another.

Hope that helps.

Spudley
  • 166,037
  • 39
  • 233
  • 307
  • Hey Spudley, I really liked your explanation and I'm glad you didn't give it all away. – DudeGuy Oct 22 '12 at 23:59
  • @all I'm still working... I'll let you guys know, when I have something that is actually an improvement. My thinking is to use an Xzibit function (a function in a function) and then have another function play with the images. Thanks again for your help. – DudeGuy Oct 23 '12 at 01:20
1

Well first, having a still tenuous grasp of how Immediately-Invoked Function Expressions work, I tried this:

var i=0;

do {
    ++i;
    (function(iteration){
        $.get('./slides/phone0' + i + '.png',function(){
            images[(i-1)] = './slides/phone0' + i + '.png';
        });
    })(i);
}
while ( images[(i-1)] == './slides/phone0' + i + '.png' );

to try to force the evaluation of the GET within the while loop... to no avail. I have settled on the following. Any thoughts on how effective/clean this is are welcome. I might be thinking from too much of a C perspective (functions in functions! -- insert Xzibit meme here). But this does get the job done:

var i=0;

function createArray(){
    $.get('./slides/text0' + (i+1) + '.txt',function(data){
        texts.push(data);
    });
    $.get('./slides/phone0' + (i+1) + '.png',function(){
        images[i] = new Image();
        images[i].src = './slides/phone0' + (i+1) + '.png';
        i++;
        createArray();
    }).error(function(){
        slider();
    });
}
DudeGuy
  • 77
  • 1
  • 7