5

I'm new to ajax and JavaScript. What am trying to do is to call an ajax function several times to fetch certain data from a resource and then "push" all of the data into an array so that I can use it later on in the code. Here is my code.

var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

for (i = 0; i < users.length; i++) {
    $.ajax({
        url: "https://api.twitch.tv/kraken/streams/" + users[i],
        success: function(data) {
            arr.push(data);
        },
        error: function(data) {
            arr.push("blank");
        },
        complete: function() {
            if (i == users.length) {
                console.log(arr); //This seem to print even when the condition isn't true
            }
        }
    });
}

The problem with the code is that, it prints to the console even when i isn't equal to users.length

My question is; how do I make certain that it waits until i == users.length is true before it prints to the console? Please keep in mind that I still desire the process to be asynchronous.

Yosvel Quintero
  • 18,669
  • 5
  • 37
  • 46
Fortune
  • 1,243
  • 2
  • 19
  • 22
  • 3
    AJAX is Asynchronous( as per your code though) i.e., you'll never know when you'll get the response. So I feel like this is not the right approach. But what you might need is `async:false` [ * NOT THE RIGHT WAY TO DO THIS * ] – Magesh Kumaar Aug 19 '16 at 10:37
  • Is this what you are looking for ? http://stackoverflow.com/questions/5052543/how-to-fire-ajax-request-periodically – TrzasQ Aug 19 '16 at 10:39
  • 1
    Also relevant - that condition is not what you think it is. Here is [some more information](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example). – VLAZ Aug 19 '16 at 10:39
  • Your if statement will never be true either, `i` will never equal the array length. – ste2425 Aug 19 '16 at 10:39
  • @MageshKumaar Just call ajax as async:false – Pablo Aug 19 '16 at 10:40
  • @Pablo that's what I said? – Magesh Kumaar Aug 19 '16 at 10:41
  • If you really need the value of "i" you'll need to make a synchronous call, thinking you cannot change the api – Ricardo Pontual Aug 19 '16 at 10:41
  • @MageshKumaar Sorry, didn't see. – Pablo Aug 19 '16 at 10:42
  • @RicardoPontual erm, not true. If you use a closure, you'd have access to the correct values from the call time. – VLAZ Aug 19 '16 at 10:42
  • `i` in your case will be set to last value because ajax call will be executed when for loop will already end and in javascript var in for loop don't create new scope only function does. This is standard closure in for loop, see [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/q/750486/387194) – jcubic Aug 19 '16 at 11:14

7 Answers7

8

Here is how you can call all requests considering their success without running an request over another:

var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

var runRequests = function(userIndex) {
  if (users.length == userIndex) {
    console.log("runRequests Success", arr);
    return;
  }

  var user = users[userIndex];

  $.ajax({
    url: "https://api.twitch.tv/kraken/streams/" + user,
    success: function(data) {
      arr.push(data);
    },
    error: function() {
      arr.push({});
      console.error("runRequests Error", "user", arguments);
    },
    complete: function() {
      runRequests(++userIndex);
    }
  });
};

runRequests(0);

Working demo

DontVoteMeDown
  • 21,122
  • 10
  • 69
  • 105
3

This happens because of the asynchronous nature. By the time the ajax callbacks are called, i == user.length is already true. But in your case you can just change:

if (i == users.length) {

to:

if (arr.length == users.length) {

This will also work nice if the callbacks are not executed in the same order as you initiated the ajax requests.

trincot
  • 317,000
  • 35
  • 244
  • 286
1

Use a closure indise the loop and bound it with i

var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

for (i = 0; i < users.length; i++) {
(function(x){
 $.ajax({
    url: "https://api.twitch.tv/kraken/streams/" + users[x],
    success: function(data) {
      arr.push(data);
    },
    error: function(data) {
      arr.push("blank");
    },
    complete: function() {
      if (i == users.length) {
        console.log(arr); //This seem to print even when the condition isn't true
      }
    }
  });
})(i)
}
brk
  • 48,835
  • 10
  • 56
  • 78
1

Can you try below code:

var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2", "cretetion", "freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var currentIndex = 0;
PerformAjax(arr, users, currentIndex);

function PerformAjax(arr, users, currentIndex)
{
    $.ajax({
        url: "https://api.twitch.tv/kraken/streams/" + users[currentIndex],
        success: function(data) {
            arr.push(data);
        },
        error: function(data) {
            arr.push("blank");
        },
        complete: function() {
            currentIndex++;
            if (currentIndex == users.length) {
                console.log(arr); //This seem to print even when the condition isn't true
            }
            else
            {
                PerformAjax(arr,users,currentIndex);
            }
        }
    });
}
vijayP
  • 11,432
  • 5
  • 25
  • 40
  • Please can you explain what the statement `PerformAjax(arr, users, currentIndex);` does? – Fortune Aug 19 '16 at 10:54
  • as we have written a separate function to perform ajax call; we are at first set the `currentIndex =0` and call this function. Now inside that function; ajax call gets executed for current user and on `complete:` we move to next user via `currentIndex++;` and perform the same ajax for next user till the time we reach end of user array. I hope this flow is clear. – vijayP Aug 19 '16 at 10:58
  • Excellent.... Works as expected. Thank You! – Chandan Kumar Nov 27 '18 at 11:42
0

The easiest way is to use a closure. Whenever you have something asynchronous in a loop, it is the same thing.

for (var i .....) {
  async(function() {
    use(i);
  }
}

In this pseudocode snippet, the inner function captures the storage location referenced by i. The loop runs, the i increments to its final value, and then the async callbacks start getting called, all of them looking up the exact same location (not value).

The general solution is this:

for (var i .....) {
  (function (i) {
    async(function() {
      use(i);
    });
  })(i);
}

i.e. wrap the whole contents of your loop in an self-executing function.

Here, the value of outer i gets passed into the wrapping self-executing anonymous function; this unique value's location gets captured by the async callback. In this way, each async gets its own value, determined at the moment the self-executing function is invoked.

Haresh Vidja
  • 8,340
  • 3
  • 25
  • 42
0

Try Below code it's working fine.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2","cretetion","freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];

for (i = 0; i < users.length; i++) {
$.ajax({
  url: "https://api.twitch.tv/kraken/streams/" + users[i],
  async: false,
  success: function(data) {
    arr.push(data);
  },
  error: function(data) {
    arr.push("blank");
  },
  complete: function() {

    if (i == (users.length-1)) { 
      console.log(i+'---x--'+users.length); //This seem to print even when the condition isn't true

    }
  }
});
}
</script>

Please have look below code wihout loop and asyns:false, tested working fine.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
var arr = [];
var users = ["brunofin", "comster404", "ESL_SC2", "OgamingSC2","cretetion","freecodecamp", "storbeck", "habathcx", "RobotCaleb", "noobs2ninjas"];
var i = 0;
var getUserData = function(arrUsers) {
   if(i<=arrUsers.length && arrUsers[i] != undefined){
    console.log(arrUsers[i]);
         $.ajax({
          url: "https://api.twitch.tv/kraken/streams/" + arrUsers[i][i],
          async: false,
          success: function(data) {
            arr.push(data);
          },
          error: function(data) {
            arr.push("blank");
          }
        });
    i ++;
    getUserData(users);
   }else{
     console.log(arr); 
   }
}

getUserData(users);
</script>
Ravi Shankar
  • 1,559
  • 1
  • 11
  • 15
-1

you can try by calling ajax with async false as below

$.ajax({
        type: "GET",
        url: remote_url,
        async: false,
        success : function(data) {
            remote = data;
        }
    });
Jekin Kalariya
  • 3,475
  • 2
  • 20
  • 32