开发者

Why is the Array undefined? [duplicate]

开发者 https://www.devze.com 2023-03-10 08:18 出处:网络
This question already has answers here: Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
This question already has answers here: Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference (7 answers) Closed 4 years ago.

When I alert my array "markerArray" outside the geocoder-function it says it's undefined.

Can't figure out why? Is there a way to get the values from the array outside the function?

var markerArray = new Array();
for(var i in opts.markers)
{
    address = opts.markers[i].address;
    //alert(opts.marke开发者_如何学JAVArs[i].icon);
    var geocoder = new google.maps.Geocoder();

    geocoder.geocode({ address: address }, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK && results.length) {
            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
                map.setCenter(results[0].geometry.location);
                var marker = new google.maps.Marker({
                    position: results[0].geometry.location,
                    map: map
                });
            }
        }
        markerArray[i] = marker;

    });

}
alert(markerArray[0].position);


I suspect it's not markerArray that it's complaining about, but markerArray[0] that's undefined.

You're calling an asynchronous API using functions you create in a loop. Those functions are closures. They each have an enduring reference to the i variable, not a copy of the value as it was when the function was defined. So all of the functions use the last i value from the loop, because none of them runs until the loop is over. So if the last value i has in the loop is 5, say, then all of the functions will use 5.

Also, you're then doing your alert too soon, before any of the callbacks has a chance to run. You'll need to do whatever final processing you need to do in one of the callbacks (you can use a counter to know when they've all happened).

You can fix both the markerArray problem and the premature alert like this:

var markerArray = new Array();
var callcounter = 0;
for(var i in opts.markers)
{
    address = opts.markers[i].address;
    //alert(opts.markers[i].icon);
    var geocoder = new google.maps.Geocoder();

    ++callcounter;
    geocoder.geocode({ address: address }, buildCallback(i));

}

function buildCallback(index) {
    return function(results, status) {
        if (status == google.maps.GeocoderStatus.OK && results.length) {
            if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
                map.setCenter(results[0].geometry.location);
                var marker = new google.maps.Marker({
                    position: results[0].geometry.location,
                    map: map
                });
            }
        }
        markerArray[index] = marker;
        if (--callcounter === 0) {
            // This was the last outstanding call
            alert(markerArray[0]); // Always assuming there was a `0` in `opts.markers`
        }
    };
}

Now, the callbacks are closures over the index argument you pass into the buildCallback function, instead of the i variable in your main loop. We do the alert when we're done with all the callbacks, which we know because of the callcounter (see note below if your "race condition" radar is going off).

All of this is because of the way closures work. They're not complicated (in fact, I wrote a blog post about them called Closures are not complicated), but there are some things you need to firmly understand to "get" why they do what they do.

Separately: You're using for..in to loop through opts.markers, which I suspect is an array. If it is, then that code has issues you need to solve. for..in is not for looping through the indexes of an array, it's for looping through the property names of an object. More here. You either need to add some checks to your for..in loop, or just use a boring old-fashioned for loop.


Re the counter: To anyone used to multi-threaded programming, my simple "increment it when scheduling, decrement it when processing" logic looks like it sets up a race condition (what if the first one gets called back before the second one is scheduled?). But it's not a race condition in JavaScript on browsers, because JavaScript on browsers is single-threaded (or if you use web workers, kind of cooperatively multi-threaded). There is no pre-emptive multi-threading here. None of the callbacks will ever be called until they have all been scheduled.


Off-topic: Although var markerArray = new Array(); works just fine, I'd recommend var markerArray = []; instead. It's shorter; for various reasons the implementation can optimize it a bit more (not that it really matters); and there's no possibility that someone has shadowed the Array symbol. Similarly, any time you want just a blank object, use {} instead of new Object().

0

精彩评论

暂无评论...
验证码 换一张
取 消