0

I'm having trouble breaking out of a for loop.

Right now, the loop does a jQuery getJSON call on every page. If the page has data, it logs all the items. If the page doesn't have data, the loop should stop entirely.

However, whenever I run this, it loops through all 100 pages. Once hitting a page with no data, it logs "this page has no content" but continues to run the for loop. (Thus ending up with an endless stream of "this page has no content" in my console)

What am I doing wrong?

I want to avoid throwing an i = NOTANINTEGER to stop the loop

for (var i=0; i< 100; i++)
  {
    var breakForLoop = false;
    var API = "http://www.foo.com/bar.json?page=" + i;

    $.getJSON(API,  function(data) {
      var items = {};
      // logs items from page run only when the page has data
      if (data.length > 0) {
        $.each( data, function(i, item) {
          console.log(item.text);
        });
      }
      // the page has no data, therefore remainder pages have no data too
      else {
        console.log("this page has no content");
        breakForLoop = true;
        return;
      }
    });

   if (breakForLoop) {
       break;
   }
}
Michelle
  • 2,702
  • 2
  • 23
  • 35
  • 1
    The problem here is the asynchronous nature of ajax – Arun P Johny Nov 03 '13 at 16:47
  • possible duplicate of [How to return the response from an AJAX call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-ajax-call) – JJJ Nov 03 '13 at 16:47
  • 4
    This is a classic example of trying to do synchronous things from inside an asynchronous callback. – josh3736 Nov 03 '13 at 16:47
  • 1
    I don't think you want ajax requests inside a for loop... – Anthony Nov 03 '13 at 16:48
  • YIKES. Just read the page. And I spent all morning thinking it was a loop problem. *facepalm* – Michelle Nov 03 '13 at 16:50
  • just send one json, with all the data (gzipped up to 100kb though) at the beginning of the session, force cache for the session (or more), bye bye crazy loop – mikakun Nov 03 '13 at 16:58
  • I wanted to write an answer which uses promises to queue the Ajax calls, but in this case a recursive solution is actually simple (I think). Here is the explanation from my answer: *Ajax requests are asynchronous. The loop doesn't wait for one of the requests to finish. So presumably, even before the response for the first request was received, to loop already did its 100 iterations.* – Felix Kling Nov 03 '13 at 16:59
  • i mean seriously why all those http request when one could (probably) do – mikakun Nov 03 '13 at 17:01
  • @FelixKling Thanks, read your answer. Are the promises that you're talking about something that node.js handles very well by any chance? – Michelle Nov 03 '13 at 17:02
  • 1
    @Michelle: Node.js uses a different approach (normal callbacks), but there are libraries which bring promises to Node.js: https://github.com/kriskowal/q. This is also a very insightful article about Node.js and promises: http://blog.jcoglan.com/2013/03/30/callbacks-are-imperative-promises-are-functional-nodes-biggest-missed-opportunity/. – Felix Kling Nov 03 '13 at 17:03
  • @mikakun do you mean calling a single request to host first, and then looping through the pages after? – Michelle Nov 03 '13 at 17:04
  • I mean one request to get all the data at once, then whatever you have to do with the data you do it, little slower to load first but then so much faster... 1 request instead of 100; with nowadays speed, the slowest part is the request not the download – mikakun Nov 03 '13 at 17:44

1 Answers1

3

One possible solution is to use something like

//this method tries to fetch data from page `x`, if there is data in the current page then it will again try to fetch data from page `x + 1`
function fetchData(page) {
    page = page || 0;
    var API = "http://www.foo.com/bar.json?page=" + page;

    $.getJSON(API, function (data) {
        var items = {};
        // logs items from page run only when the page has data
        if (data.length > 0) {
            $.each(data, function (i, item) {
                console.log(item.text);
            });
            //since there is data in the current page try to fetch data from page page + 1
            fetchData(page + 1);
        }
        // the page has no data, therefore remainder pages have no data too
        else {
            console.log("this page has no content");
        }
    });
}

//initiage fetching data
fetchData(0) ;
Arun P Johny
  • 384,651
  • 66
  • 527
  • 531