2

I'm trying to iterate over an array and assign a variable with a for loop. So something like this:

function Person(name, status){
  this.name = name;
  this.status = status;
}

var status = [];
var array = ["bill","bob","carl","ton"];
function exAjax(function(){
 for(var i = 0; i < array.length; i++){
   var name = array[i];
   console.log(name); =====> this gives the correct name

   $.ajax({
     url: xxxxxxx,
     success: function(data){
       if(data.stream === null){
         var person = new Person(name, "dead");
         console.log(name); =====> return undefined until the last
         person

         status.push(person);       
       }
     }

   })
   name = "";
 }
})

The problem I'm having is that name is not getting into the success function. I thought js keeps traveling upwards to look for the variable if it doesn't exist in it's current scope? I'm getting undefined for the name variable if I try to console.log name! Scope masters what am I doing wrong?

9
  • 1
    AJAX is asynchronous. By the time the success function runs, the for loop has already finished. What do you think does the value i have after the loop is finished? Commented Aug 4, 2016 at 4:32
  • Possible duplicate of JavaScript closure inside loops – simple practical example Commented Aug 4, 2016 at 4:33
  • Thank you! If I set async: "false" ; for that ajax call will that fix the problem? Commented Aug 4, 2016 at 4:33
  • That’s a really horrible way of solving it. Instead, wrap your AJAX call in a closure like this: (function(i){})(i);. Commented Aug 4, 2016 at 4:34
  • Ok. Thanks again. I will try that. Commented Aug 4, 2016 at 4:35

2 Answers 2

4

You can use .queue(), $.map() to maintain scope of name. Also, change status array to an object having property status where value is an array to prevent possible conflict with this.status of Person object.

Note, you can also chain .promise(/* queueName */) to perform tasks at .then() when all queued functions in queueName, i.e.g., "status" have been called, queueName .length is 0.

function Person(name, status){
  this.name = name;
  this.status = status;
}

var blob = new Blob(['{"stream":null}'], {type:"application/json"});
var url = URL.createObjectURL(blob);
// change `status` array reference, e.g., to `arr`
var arr = {status:[]};
var array = ["bill","bob","carl","ton"];

$(arr).queue("status", $.map(array, function(curr) {
  return function(next) {
    var name = curr;
    // do asynchronous stuff
    $.ajax({url:url, dataType:"json"})
    .then(function(data) {
       if(data.stream == null){
         var person = new Person(name, "dead");
         console.log(name, person);
         arr.status.push(person);
       }
    })
    .then(next) // call next function in `"status"` queue
  }
}))
.dequeue("status")
.promise("status")
// do stuff when all functions in `"status"` queue have completed,
// `"status"` queue `.length` is `0`
.then(function() {
   // `this` : `arr` as jQuery object
   // `this[0].status`: array containing objects pushed to `arr.status`
   console.log(this[0].status); // $(this).prop("status");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>

jsfiddle https://jsfiddle.net/nnayjckc/2/


You can alternatively use $.when(), .apply(), $.map(), to return same result

function Person(name, status) {
  this.name = name;
  this.status = status;
}

var blob = new Blob(['{"stream":null}'], {
  type: "application/json"
});
var url = URL.createObjectURL(blob);
// change `status` array reference, e.g., to `arr`
var arr = {
  status: []
};
var array = ["bill", "bob", "carl", "ton"];

$.when.apply($, $.map(array, function(curr) {
  var name = curr;
  return $.ajax({
      url: url,
      dataType: "json"
    })
    .then(function(data) {
      if (data.stream == null) {
        var person = new Person(name, "dead");
        console.log(name, person);
        arr.status.push(person);
      }
    })
}))
.then(function() {
  console.log(arr.status)
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">  
</script>

jsfiddle https://jsfiddle.net/nnayjckc/3/

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for the thorough explanation. I appreciate it!
0

That's because $.ajax perform an asynchronous HTTP (Ajax) request. It means that your for loop won't wait for success to complete. Instead it will continue with its iteration.

One way (of the many possible solutions), is to make this $.ajax synchronous with the async: false option

From the documentation

async (default: true)
Type: Boolean
By default, all requests are sent asynchronously (i.e. this is set to true by default). If you need synchronous requests, set this option to false.

 for(var i = 0; i < array.length; i++){
   var name = array[i];
   console.log(name); =====> this gives the correct name

   $.ajax({
     url: xxxxxxx,
     async: false,
     success: function(data){
       if(data.stream === null){
         var person = new Person(name, "dead");
         console.log(name); =====> return undefined until the last
         person

         status.push(person);       
       }
     }

   })
   name = "";
 }
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.