0

我正在开发一个了解某人某些兴趣的移动应用程序,例如城市。例如,应用程序知道纽约。

我想在 panoramio api 中搜索纽约的照片,但为此我必须知道纽约市的坐标。所以我做了以下事情:

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

这是在一个 for 循环中,该循环循环某个人的兴趣数量。places[j].name是兴趣的名称。distance是我想要图片来源的门槛。到目前为止,一切都很好。

然后我想将所有信息收集到 requestPlaces 数组中。requestPlaces 声明如下:

var requestPlaces = [];

我将所有执行的请求推送到该数组中,以便稍后我可以执行以下操作:

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

});

这就是问题所在。在$.when.apply(null, requestPlaces)我得到大小 0 和参数是一个空数组。似乎它没有等待所有请求完成并推送到 requestPlaces 数组。我能做些什么来解决这个问题?

几个考虑:

此代码块在另一个 $.when.apply 块内,用于另一个获取人们兴趣的请求,这是有效的。

而且我知道我对 panoramio api 所做的请求正在返回成功的结果,并且与第一个输入无关$.when.apply(null, requestPlaces)

如果需要,完整的代码可以更好地理解:

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);
});
4

1 回答 1

1

João,您的问题出现是因为geocoder.geocode(...)并且$.ajax(...)是在异步系列中执行的。该数组requestPlaces正确地累积了$.ajax(...)Promise,但它仅外部geocoder.geocode(...)请求及其相应的$.ajax(...)请求返回后才会这样做。因此,在 for 循环结束时,requestPlaces保证仍然为空 - 所有geocoder.geocode(...)请求都将发出,但保证它们的$.ajax(...)请求尚未发送,更不用说返回了。

解决方案其实很简单;用复合承诺填充数组requestPlaces,每个承诺都是由 (in pseudocode) 返回的承诺geocode(...).then($.ajax(...))

试图在主程序中做所有事情是相当令人困惑的。将异步任务分离为实用程序功能变得更加清晰,如下所示:

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

请注意,这两个实用程序都返回承诺。

第三个实用程序可帮助提供有意义的地理编码错误消息:

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 || '';
}

现在,简化的主例程最好表述为 a .mapof places,返回所需的承诺数组,如下所示:

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

请注意,失败案例可以这样传递到整个链条。但是,您真的不希望任何单独的地理编码/全景失败(包括“ZERO_RESULTS”)破坏主例程中的整个 $.when() 企业。将失败作为(可检测的)成功传递是 jQuery 缺少 $.allSettled() 方法的简单解决方法。

$.when.apply(...)是这样的:

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.
        }
    });
});

未经测试 - 可能需要调试

编辑

您可能需要测试result.photos而不是$.isPlainObject(result). 以下版本也将反向运行结果,处理每一秒的结果并忽略其他结果。

$.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.
        }
    }
});
于 2014-08-12T14:27:28.023 回答