如何处理对循环

时间:2016-01-15 09:06:27

标签: javascript jquery ajax promise

我需要从后端收集不同设备的日志,然后将其导出到csv文件。问题是设备的数量可能会有所不同。所以我向后端询问设备数量并循环查看日志请求。

问题是for...循环的运行速度比我获得$.post的响应速度快得多,所以在得到响应之前循环结束。经过一些研究,我可以处理这种行为,但现在我被要求将数据添加到请求中,对此我没有存储相应设备的参考。所以我在外部js文件中添加了我需要轮询的设备名称和点,所以我有一个定义的列表可以循环。

我尝试使用for循环的索引来获取设备名称,这不起作用,因为循环太快了。现在我已经创建了一个解决方法,通过定义计数器并将设备推送到另一个变量中。这不会感觉“干净”,应该有更好的方法来轮询数据并跟踪它是哪个设备。

到目前为止的代码:

     function collectData() {
        var outString = "";
        var lots of stuff I can pre-fetch

        var logs = function (outString, saveCSV) {
           var postString;
           var devices = [];
           var count = 0;

           for (i = 1; i <= maxDevice; i++) {

              postString = build postString in loop
              devices.push(spots[0][i - 1]);

              $.post('/path/foobar.db',
                      postString,
                      function (data) {
                         outString += "Spotlist for: " + spots[0][count] + "\n";
                         count++;
                         outString += data.replace(/{/g, "").replace(/}/g, "").replace(/:/g, ";").replace(/,/g, "\n").replace(/\"/g, "");
                         outString += "\n\n";
                      });

              postString = "/path/eventlog.csv?device=" + i;
              $.get(postString,
                      function (data) {
                         outString += "Event Log: \n" + data + "\n";
                      });

              postString = "/path/errorlog.csv?device=" + i;
              $.get(postString,
                      function (data) {
                         outString += "Error Log: \n" + data + "\n";
                      });
           }

           $(document).ajaxStop(function () {
              saveCSV(outString, filename);
              $(this).unbind('ajaxStop');
           });
        };

        var saveCSV = function (outString, filename) {
           var tempString = "data:text/csv;charset=utf-8," + outString;
           var encodedUri = encodeURI(tempString);

           var a = document.getElementById("dlLink");

           if (window.navigator.msSaveOrOpenBlob) {
              blobObject = new Blob([outString], {type: 'text/csv;charset=utf-8'});
              window.navigator.msSaveBlob(blobObject, filename);
           } else
           {
              a.setAttribute("href", encodedUri);
              a.setAttribute("download", filename);
              a.click();
           }
        };

        outString = lots of predefined and pre-fetched stuff
        outString += "Device data: \n\n";

        logs(outString, saveCSV);
     }

我不满意的部分是:

          for (i = 1; i <= maxDevice; i++) {

              postString = "get = {" + i + ":en:[";
              for (j = 0; j < spots[i].length; j++) {
                 postString += '"' + spots[i][j] + '",';
              }
              postString = postString.slice(0, -1) + "]}";
              devices.push(spots[0][i - 1]);

              $.post('/path/foobar.db',
                      postString,
                      function (data) {
                         outString += "Spotlist for: " + spots[0][count] + "\n";
                         count++;
                         outString += data.replace(/{/g, "").replace(/}/g, "").replace(/:/g, ";").replace(/,/g, "\n").replace(/\"/g, "");
                         outString += "\n\n";
                      });

要输出我收集的设备,我使用计数器来跟踪设备名称。我有预感这不是最好和“最干净”的方法,所以我想问一下,在收集制作帖子的正确设备方面是否有更好的方法来处理异步(sp?)如果一切都已完成,还要触发DL。

由于我的问题似乎并不清楚,或许我需要缩小范围。代码有效,但似乎只是被我修改,应该有更清洁的方法来

A)处理posts / gets,因为CSV的字符串只是按照请求被回答的方式放在一起,所以设备1不是csv中的第一个,而是第一个。 $(document).ajaxStop等待完成所有事情,但不能以正确的顺序完成。

B)我需要将for循环的索引与我为数据轮询的设备相关联。我使用了额外的变量,我想要通过一个额外的数组。还有更好的办法吗?

2 个答案:

答案 0 :(得分:2)

问题是你需要按顺序运行在获得对AJAX调用的响应后调用的方法。

要做到这一点,你必须承诺所有jQuery AJAX调用都会返回promises。您可以执行以下操作,而不是将代码作为参数传递:

var functionToRunAfterResponse(params) = function(params) {
   // do something with params
};

var promise = $.post(/*...*/); // some kind of ajax call

promise.then(functionToRunAfterResponse);

请注意,functionToRunAfterResponse将接收响应数据作为参数。即它相当于:

promise.then(function(reponseData) {
  functionToRunAfterResponse(responseData);
});

这是一个简单的通话方式。请参阅$.thendeferred以及promise文档。

如果你要打一堆电话,你必须做以下事情:

  • 存储AJAX调用的promise,例如在数组
  • 检查所有承诺是否得到满足(即所有回复都已到达)
  • 按所需顺序运行代码。即按正确的顺序运行promise.then以获得所需的结果。

为此,您可以使用$.when

代码结构应该是这样的(伪代码):

var promises = [];
// Make the calls, and store the promises
for(/**/) {
  promises.push( $.post or some other kind of ajax call );
}
// Ensure that all the responses have arrived
$.when.apply($, promises)  // See below note
.then(function() {
   for each promise in promises
      promise[i].then(function(reponseData) {
         // run the necessary code
      });

注意:Here you've got a deeper explanation,但基本上,由于$.when需要一系列承诺,例如:$.when(promise1, promise2, ...)并且您有一个数组promises[],您必须使用申请进行调用,以便数组中的项目作为单独的参数传递。

最终说明:

1)考虑到AJAX调用可能会失败。在这种情况下,您将获得失败的承诺,而不是已解决的承诺。你应该检查一下。如果任何承诺失败,$.when将无法按预期运行:

  

方法[when]将在所有Deferred解决后立即解析其主延期,或者在其中一个Deferred被拒绝后立即拒绝主延期。

2)为了处理错误,then有两个参数:

$.when.apply().then(function() { /* success */ }, function() { /* error */ });

3)当你按照我的解释进行呼叫时,它们都是并行执行的,并且响应将以任何顺序到达。即不保证您将按照拨打电话的顺序收到所有回复。他们甚至可能会失败,正如我在1)中所解释的那样,这就是为什么你必须使用when并再按顺序运行

4)使用异步方法很棒,但是你有责任检查它们是否没有失败,并按正确的顺序运行你得到响应后需要运行的代码。

答案 1 :(得分:0)

我无法理解你的问题,但主要问题是异步,对吧?

尝试异步调用函数(摘要版本):

// Function that manage the data from ajax
var myResponseFunction = function (data) {
  $('#response').html('data: ' + JSON.stringify(data));
};

// Ajax function, like your post with one parameter (add all you need)
function myAJAXFunction(callback) {
    $.ajax({
        type: 'POST',
        url: '/echo/json/',
        dataType: 'json',
        data: {
            json: JSON.stringify({
                'foo': 'bar'
            })
        }
    }).done(function(response) {
      // Done!! Now is time to manage the answer
      callback(response);
    }).fail(function (jqXHR, textStatus, errorThrown) {
      window.console.error('Error ' + textStatus + ': ' + errorThrown);
    });
}

// Usually, this function it's inside "document.ready()".
// To avoid the ajax problem we call the function and "data manage function" as parameter.

for (i = 1; i <= maxDevice; i++) {
    myAJAXFunction(myResponseFunction);
}

https://jsfiddle.net/erknrio/my1jLfLr/

这个例子是西班牙语,但在这个答案中,您的代码用英语注释:)。

对不起我的英文:S。