João
João

Reputation: 341

wait for all the ajax request to panoramio api inside google geocoder to finish and then perform something

I am developing a mobile application that knows certain interests of a person, like cities for example. So the application knows New York for example.

I want to search for new york photos in panoramio api but for that i have to know the coordinates of New York City. so i do the following:

geocoder.geocode( { 'address': places[j].name}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK){
        var request = $.ajax({type:"GET",
                             url: "http://www.panoramio.com/map/get_panoramas.php?set=public&from=0&to=20&minx="+ (results[0].geometry.location.lng()-distance) +"&miny="+ (results[0].geometry.location.lat()-distance)+"&maxx="+ (results[0].geometry.location.lng()+distance) +"&maxy="+ (results[0].geometry.location.lat()+distance) +"&size=medium&mapfilter=true",
                             dataType: "jsonp",
                             success : function(){
                                   if(typeof places[j] !== "undefined")
                                        succeeded.push(places[j].name);
                                   }
                             });
                            requestPlaces.push(request);
    }
});

this is inside a for loop that loops for the amount of interests a certain person has. places[j].name is the name of an interest. distance is the treshold from the are where i want the pictures to be from. So far so good.

Then i want to gather all the information into the requestPlaces array. requestPlaces is declared as follows:

var requestPlaces = [];

and i push all the performed requests into that array so later i can do:

$.when.apply(null, requestPlaces).done(function () {
    console.log("tamanho do request places: dentro do when apply" + requestPlaces.length);
    console.log(arguments);

});

and here is the problem. inside the $.when.apply(null, requestPlaces) i am getting size 0 and arguments to be an empty array. It seems that it is not waiting for all the requests to be done and pushed to the array of requestPlaces. What can i do to solve this?

Several considerations:

this block of code is inside another $.when.apply block for another request to get the persons interests, which is working.

and also i know as a fact that the request i am doing to panoramio api is returning successfull results and has nothing to do with entering in the $.when.apply(null, requestPlaces) first.

Full code for betterunderstanding if needed:

for(var j = 0; j<places.length; j++){
    geocoder.geocode( { 'address': places[j].name}, function(results, status) {
            if (status == google.maps.GeocoderStatus.OK){

                var request = $.ajax({
                        type:"GET",
                        url: "http://www.panoramio.com/map/get_panoramas.php?set=public&from=0&to=20&minx="+ (results[0].geometry.location.lng()-distance) +"&miny="+ (results[0].geometry.location.lat()-distance) +"&maxx="+ (results[0].geometry.location.lng()+distance) +"&maxy="+ (results[0].geometry.location.lat()+distance) +"&size=medium&mapfilter=true",
                        dataType: "jsonp",
                        success : function(){
                             if(typeof places[j] !== "undefined")
                                 succeeded.push(places[j].name);
                        }
                });
                requestPlaces.push(request);
        }
    });

}

$.when.apply(null, requestPlaces).done(function () {
        console.log("size" + requestPlaces.length);
        console.log(arguments);
});

Upvotes: 0

Views: 395

Answers (1)

Roamer-1888
Roamer-1888

Reputation: 19288

João, your problem arises because geocoder.geocode(...) and $.ajax(...) are performed in asynchronous series. The array requestPlaces correctly accumulates the $.ajax(...) promises but it only does so after the outer geocoder.geocode(...) requests and their corresponding $.ajax(...) requests return. Therefore, at the point where the for loop finishes, requestPlaces is guaranteed still to be empty - all the geocoder.geocode(...) requests will have been made but their $.ajax(...) requests are guaranteed not to have been sent yet, let alone to have returned.

The solution is actually quite simple; populate the array requestPlaces with composite promises, each one being the promise returned by (in pseudocode) geocode(...).then($.ajax(...)).

Trying to do everything inside the master routine is rather confusing. It becomes much clearer with the async tasks separated out as utility functions, as follows :

function doGeocode(placeName) {
    return $.Deferred(function(dfrd) {
        geocoder.geocode({ 'address': placeName }, dfrd.resolve);
    }).promise();
}

function doPanoramio(loc) {
    return $.ajax({
        url: "http://www.panoramio.com/map/get_panoramas.php",
        type: "GET",
        data: {
            'set: 'public',
            'from': 0,
            'to': 20,
            'minx': loc.lng() - distance,
            'miny': loc.lat() - distance,
            'maxx': loc.lng() + distance,
            'maxy': loc.lat() + distance,
            'size': 'medium',
            'mapfilter': 'true'
        }
        dataType: "jsonp"
    });
}

Note that both these utilities return promises.

And a third utility to help provide meaningful geocode error messages :

function inverseLookup(obj, value, k) {
    // Find an object's key from a given value.
    $.each(obj, function(key, val) {
        return (val !== value) ? true : !(k = key);//continue or break
    });
    return k || '';
}

Now, the simplified master routine is best phrased as a .map of places, returning the required array of promises, as follows :

var requestPlaces = $.map(places, function(place) {
    if(place.name) {
        return doGeocode(place.name).then(function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                return doPanoramio(results[0].geometry.location).then(null, function(xhr, textStatus) {
                    return $.when(place.name + ': ' + ': panoramio failure : ' + textStatus);
                });
            } else {
                return $.when(place.name + ': ' + ': geocode failure : ' + inverseLookup(google.maps.GeocoderStatus, status, '?'));
            }
        });
    } else {
        return null;
    }
});

Note that failure cases could be passed down the chain as such. However, you don't really want any individual geocode/panoramio failure (including "ZERO_RESULTS") to scupper the whole $.when() enterprise in the master routine. Passing on failures as (detectable) success is a simple workaround for jQuery's lack of an $.allSettled() method.

And the $.when.apply(...) will be something like this :

console.log("size: " + requestPlaces.length);

$.when.apply(null, requestPlaces).then(function () {
    console.log("All geocoding and panoramio requests complete");
    $.each(arguments, function(i, result) {
        if($.isPlainObject(result)) {
            //result is a Panoramio response - process the result.photos array as required.
        } else {
            console.error(result);//if result is not a plain object, then it's an error message.
        }
    });
});

untested - may need debugging

EDIT

You probably need to test for result.photos instead of $.isPlainObject(result). The following version will also run through the results backwards, processing every second result and ignoring the others.

$.each(Array.prototype.slice.apply(arguments).reverse(), function(i, result) {
    if((i % 2) === 0) {
        if(result.photos) {
            //result is a Panoramio response - process the result.photos array as required.
        } else {
            console.error(result);//if result is not a plain object, then it's an error message.
        }
    }
});

Upvotes: 1

Related Questions