如何在jquery中使用promises循环?

时间:2015-07-09 20:55:32

标签: javascript jquery ajax promise

我正在开发一个应用程序,我需要编制一份天气数据表,其中包含过去10年当天的天气信息。我正在使用Forecast.io,它提供了一个简单的API来查询旧的天气数据:

https://api.forecast.io/forecast/APIKEY/LATITUDE,LONGITUDE,TIMESTAMP

所以基本上我必须传递我需要数据的密钥,纬度,长度和时间戳。

我的代码如下:

function bindweatherHistoryTable(){
        console.log(historyData);
        var weatherHistoryTable = rivets.bind($('#weatherHistoryTable'), {data: historyData});
    }

    var historyData = {};
    for(i = 1; i <= 10; i++){
        fetchHistoryUrl = '//api.forecast.io/forecast/' + forecastToken + '/' + farmLat + ',' + farmLng + ',' + (moment().subtract(i, 'years').valueOf() / 1000).toFixed(0);

        var promise = $.ajax({
            method: 'GET',
            dataType: 'JSONP',
            url: fetchHistoryUrl,
            success: function(response){
                var data = {
                    temperature: response.currently.temperature,
                    dewPoint: response.currently.dewPoint,
                    humidity: response.currently.humidity,
                    windSpeed: response.currently.windSpeed,
                    windBearing: response.currently.windBearing,
                    pressure: response.currently.pressure
                };

                historyData[moment().year() - i] = data

                console.log(data);
            }
        });

        promise.done(function(){ console.log('hello') });

        if(i == 10){
            console.log(i);
            promise.done(bindweatherHistoryTable());
        }
    }
}

所以基本上我使用for循环查询最近10年,然后将数据编译成单个对象,最后使用Rivets将对象绑定到视图。

正在编译该对象,因为我正在记录所形成的每个ajax调用对象。在每次ajax调用成功后,我也使用promise.done()方法记录'hello'

我还指定如果它是最后一次迭代,则promise应该记录最终的历史记录对象并将其绑定到历史记录表。

每次通话都是成功的。我可以在控制台中看到每个调用的输出对象。每个承诺解决后,我也可以在日志中看到“你好”。

但是,在进行任何ajax调用之前,在最开始记录所有内容后应记录的最终对象。我是承诺的新手,我不明白为什么。我只是想通过将它绑定到我的表中来获取最终对象并在我的视图中使用它。我做错了什么,我该怎么做才能解决它?

3 个答案:

答案 0 :(得分:1)

问题是您在ajax调用完成之前正在记录i。不要忘记他们是异步调用,所以,例如,你可以有类似的东西:

var promises = 0, length = 10;
for(i = 1; i <= length; i++){
        fetchHistoryUrl = '//api.forecast.io/forecast/' + forecastToken + '/' + farmLat + ',' + farmLng + ',' + (moment().subtract(i, 'years').valueOf() / 1000).toFixed(0);

        var promise = $.ajax({
            method: 'GET',
            dataType: 'JSONP',
            url: fetchHistoryUrl,
            success: function(response){
                var data = {
                    temperature: response.currently.temperature,
                    dewPoint: response.currently.dewPoint,
                    humidity: response.currently.humidity,
                    windSpeed: response.currently.windSpeed,
                    windBearing: response.currently.windBearing,
                    pressure: response.currently.pressure
                };

                historyData[moment().year() - i] = data

                console.log(data);
            }
        });

        promise.done(function(){ 
          promises++;
          console.log(promises);
          if (promises === length) {
            bindweatherHistoryTable();
          }
        });
    }

答案 1 :(得分:1)

Promise是异步的,因此你的for循环将在它们完成之前完成。出于同样的原因,成功函数中i的值始终为10。您需要跟踪已履行的承诺数量,并在每次承诺履行时进行检查。基本概念:

var complete = 0;
function completeCallback() {
    // All promises done, bind your table, etc
}
for (var i = 0; i < 10; i++) {
    $.ajax(url).done(function () {
        complete ++;
        if (complete == 10) {
            completeCallback();
        }
    }
}

旁注:我认为如果你正在使用jQuery ajax的promises功能,你就不需要指定成功的回调。

答案 2 :(得分:1)

你可能对承诺的异步部分(以及一般的异步)感到困惑 您的for循环&#34;运行完成&#34;,异步部分($.ajax调用)正在异步处理,并且他们的成功回调将在未来的某个时间触发。所以... i在异步调用完成之前正在快速前进并达到10。相反,只有在所有请求完成后,才能在成功回调中调用bind函数。

编辑:关于historyData

中关于错误密钥的评论

比依赖于响应对象中可用数据的特定情况更通用的方法是传递&#34;上下文数据&#34;使用context配置参数中的$.ajax属性进入ajax回调。然后,该上下文可用作回调中的this对象。将其与&#34;关闭&#34;你也可以解决这个问题。

试试这个:

...
context: {year: i /*other context data can be added here if needed*/},
success: function(response) {
  var data = {
    temperature: response.currently.temperature,
    dewPoint: response.currently.dewPoint,
    humidity: response.currently.humidity,
    windSpeed: response.currently.windSpeed,
    windBearing: response.currently.windBearing,
    pressure: response.currently.pressure
  };

   historyData[moment().year() - this.year] = data;

   if(Object.keys(historyData).length == 10) // 10 properties set in historyData...
     bindweatherHistoryTable();
  }
...

您根本不需要使用promise.done