0

I'm trying to get some javascript to run a for loop which will create an xml file and then, after the loop is complete, to visit a link that would allow the user to download the file. Currently what happens is that it gets half-way through creating the file and then sends the user the link to download the file.

I followed this question's advice and created a callback variable and moved the download link to another function, however that hasn't solved the problem either. Does anyone else have any idea how to do it? My code is:

var callbackcount=0;
function populateTemplate(numResults) {
  for (i=1; i < numResults; i++) {
    $.post('linkToTheFileThatCreatesTheXMLFile', {WithSomeParameters};
    download(numResults);
  }
}

function download(numResults) {
  callbackcount++;
  if (callbackcount == numResults) {
    window.location.href="linkToPHPThatDownloadsFile";
  }
}

Thanks heaps

Edit: Console.log results:

LOG: 230 LOG: 330 LOG: 230 LOG: 330 
LOG: 430 LOG: 530 LOG: 630 LOG: 730 
LOG: 830 LOG: 930 LOG: 1030 LOG: 1130 
LOG: 1230 LOG: 1330 

If numResults = 7

LOG: 27 LOG: 37 LOG: 47 

or

LOG: 27 LOG: 37 LOG: 47 LOG: 57 

etc.

Edit: The new code:

function populateTemplate(numResults) {
  var callbackcount = 1;
  for (i=1; i < numResults; i++) {
    $.post('linkToTheFileThatCreatesTheXMLFile', {WithSomeParameters}, function() {
      callbackcount++;
      console.log(callbackcount, numResults);
      if(callbackcount === numResults - 1) {
        window.location.href="linkToPHPThatDownloadsFile";
      }
    });
  }
}
Community
  • 1
  • 1
Pete
  • 1,095
  • 3
  • 9
  • 17

2 Answers2

2

$.post is asynchronous call. So by the time you get to the last loop, where in the closure callbackcount will equal numResults all the posts won't have gone through yet. definitly not the last one.

$.post allows for a callback which is called once the post is complete. This callback should call your download methods.

var callbackcount=0;
function populateTemplate(numResults) {
  for (i=1; i < numResults; i++) {
    $.post('linkToTheFileThatCreatesTheXMLFile', {WithSomeParameters}, function() {

      callbackcount++;
      if (callbackcount === numResults) {
        window.location.href="linkToPHPThatDownloadsFile";
      }

    });
  }
}

This way you evaluate the function when ever the post is complete. Also I inlined it to make the access of the variables a little esier.

BTW: always use === over == in JavaScript See why

EDIT

The loop index i should start at 0. Also it should be declared as a local variable, not global.

for ( var i = 0; i < numResults; i++ )

Otherwise the loop will only run 5 times with a numResults of 6.

EDIT 2 - Second approach

If it has to do with too many requests per frame (which I doubt) You could also try this and see if it makes a difference.

var callbackcount=0;
function populateTemplate(numResults) {
  for (i=1; i < numResults; i++) {

    setTimeout( function() {

      $.post('linkToTheFileThatCreatesTheXMLFile', {WithSomeParameters}, function() {

        callbackcount++;
          if (callbackcount === numResults) {
            window.location.href="linkToPHPThatDownloadsFile";
          }

      });

    }, 100);
  }
}

this delays every request and therefore has not every request going in one frame.

Community
  • 1
  • 1
Mathias
  • 2,484
  • 1
  • 19
  • 17
  • Thanks heaps for your response I amended my code with the callback function, however it still didn't work :( I put an `alert(callbackcount)` directly underneath the `callbackcount++` and ran it with `numResults = 7`. It would then pop up with: 1,2,3,4,5 and then stop, the next time it would be 2,3,1,4 etc. Is this still because it's finishing the loop too quickly? – Pete Dec 12 '12 at 03:11
  • I looked at it again and I realized that you start the loop with a counter ov `i = 1`. in order to have `callbackcount` equal numResults you will need to set `i = 0` otherwise the loop will run one less time. See my edit. – Mathias Dec 12 '12 at 17:55
  • If that still doesn't change anything write `console.log( callbackcount, numResults );` right before the `if` statement. – Mathias Dec 12 '12 at 18:06
  • Sorry, I set i to be 1 because it's referencing an array starting from the second position not the first and I don't think that that will affect the early exiting of the loop. I put in `console.log` and tried it with 30 `numResults = 7`. I've editted my question with the results. – Pete Dec 12 '12 at 21:16
  • Ok, but if you start the loop with one then you have to change the if clause to: `if ( callbackcount === numResults - 1 )` and I don't really understand the log. It should look something like this LOG 0 7 LOG 1 7 LOG 2 7 LOG 3 7 LOG 4 7 LOG 5 7 LOG 6 7 – Mathias Dec 12 '12 at 22:16
  • can you maybe edit the question again with what the code now looks like with the console log? – Mathias Dec 12 '12 at 22:17
  • Oh, so I got lazy and copied and pasted my `numResults` code from my previous comment. It should have said "and tried it with `numResults = 30`". I've edited the post with my new code and also the output if `numResults = 7` as well... – Pete Dec 12 '12 at 22:41
  • This is confusing. Two more assumptions: Are you sure the redirect comes from this `if` clause and nowhere else. Second, maybe all the calles go through but you don't see that because the console gets cleared when you change the site. To test the second you you could try replacing th `window.location.href="...";` with a `console.log( "sending now");` statement and let me know what the output is this time. – Mathias Dec 12 '12 at 23:41
  • I did a grep and there is no other mention of a redirect anywhere else in my code. I also put the `sending now` log in but it was never called because `callbackcount != numResults` and so it won't run. – Pete Dec 13 '12 at 00:30
  • tough one! did you try the delay I added to my answer? – Mathias Dec 13 '12 at 00:34
  • I just created a jsfiddle to test the scenario and it works fine. That makes me think it is a backend issue. the jQuery callback only get's called when it's a success. When the backend can't be reached then it will never get called. – Mathias Dec 13 '12 at 00:45
  • Hmm, that didn't work either. Don't worry about it all, this is probably a really bad way of solving my problem anyway. I shouldn't be passing lots of little files asynchronously and so I'll try and find a better way around it. Thanks heaps for all your effort, sorry that it was so difficult :( – Pete Dec 13 '12 at 00:49
  • Here is the jsfiddle I created: http://jsfiddle.net/HekdT/2/. It also works with an invalid url call. Check it out. you can add an `.error()` callback to see if it has anything want wrong. See if that fiddle works for you with your url and your data. – Mathias Dec 13 '12 at 00:53
0

It looks like you are using jQuery. The code you posted is incorrect, it should be this:

$.post( 'linkToTheFileThatCreatesTheXMLFile', {Parameters}, function() {
    download(numResults);
});

(Unless your code formatting just got messed up)

also, make sure numResults is defined and initialized up by callbackcount. (might be a scoping issue)

Daniel Placek
  • 765
  • 5
  • 16