javascript, $.ajax, variable name

2019-01-27 00:08发布

问题:

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?

回答1:

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/



回答2:

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 = "";
 }
})