Luis
Luis

Reputation: 1097

Loop in function not working

I'm getting this little trouble while looping, I always get the same "y" value (5) and it doesn't decreases or increases. What I want is to repeat the ajax call X times [all] and pass the response and the current call number thru an anonymous function. I hope you can help me.

function _shds(long, count, func) {
    for (var y = 0; y < count; y++) {
        gapi.client.load('urlshortener', 'v1', function() {
            var request = gapi.client.urlshortener.url.insert({
                'resource': {
                    'longUrl': long
                }
            });
            var resp = request.execute(function(resp) {
                func(resp.id, y);
            });
        });
    }
}
var total = 3;
var base = "soem";
var lista = ["1", "2", "3", "4", "5"];
_shds("222", total, function(data, y) {
    if (data != "undefined") {
        newbase = base.replace("soem", data);
        console.log(lista[y].uid + newbase + " pos:" + y);
    }
});​

UPDATE: Sorry, here's an example of the code: http://jsfiddle.net/ZgXZB/

Upvotes: 1

Views: 2181

Answers (2)

Justin Ethier
Justin Ethier

Reputation: 134167

This is a trickier problem than it first appears, and a common JavaScript gotcha. What is going on is that when you say:

for (var y = 0; y < count; y++) {
    gapi.client.load('urlshortener', 'v1', function() {
        var request = gapi.client.urlshortener.url.insert({
            'resource': {
                'longUrl': long
            }
        });
        var resp = request.execute(function(resp) {
            func(resp.id, y);
        });
    });
}

You are creating a closure on y in the function callback that you pass to get. However, you only have created one closure and it has the final value of y which is count. What you want to do is create count different closures, so you need to introduce a new scope:

for (var y = 0; y < count; y++) {
 (function (y){      
  gapi.client.load('urlshortener', 'v1', function() {
      var request = gapi.client.urlshortener.url.insert({
          'resource': {
              'longUrl': longurl
          }
      });
      var resp = request.execute(function(resp) {
          func(resp.id, y);
      });
  });
 })(y);      
}

I believe that should do it. You need to declare a new function to create a new scope in JavaScript, and by passing y as a parameter to that function you are creating a new closure for each value of y.

Upvotes: 4

user1207456
user1207456

Reputation:

The issue is that you are trying to get the value of i after the callbacks have returned, which is after your for-loop has finished. What you need to do is bind the value of i you want to pass to the callback when you first construct your intermediate callback for $.get:

function _short(longurl, count, func) {
    for (var y = 0; y < count; y++) {
        gapi.client.load('urlshortener', 'v1', function() {
            var request = gapi.client.urlshortener.url.insert({
                'resource': {
                    'longUrl': longurl
                }
            });
            var resp = request.execute((function(y, resp) {
                func(resp.id, y);
            }).bind(this, y));
        });
    }
}
var totalchat = 10;
var base = "soem";
var listachat = ["1", "2", "3", "4", "5"];
_short("222", 3, function(data, y) {
    if (data != "undefined") {
        newbase = base.replace("soem", data);
        console.log(listachat[y].uid + newbase + " pos:" + y);
    }
});

What bind does is create a new function where the value of this is bound to the first parameter (which I just set as this) and all remaining arguments are bound to the first arguments of the function, while the function it returns takes a new set of arguments (in this case, only the data argument remains from the original function, which matches the callback signature of $.get).

Upvotes: 2

Related Questions