简体   繁体   English

JavaScript / jQuery如何在多个Ajax响应上调用回调函数

[英]How does JavaScript/jQuery invoke callback functions on multiple ajax responses

I know that the a in ajax means asynchronous, and as such there is no guarantee which order my responses will come back in, in fact its reasonable to expect the largest payload to return last. 我知道ajax中的a表示异步,因此不能保证我的响应将返回哪个顺序,实际上可以合理预期最大的有效负载最后返回。

My question however is regarding the callbacks. 但是我的问题是关于回调。 Sometimes when I notice my responses come back in a different order to which they were sent, the 'wrong' callback is invoked. 有时,当我注意到我的回复以发送顺序不同的顺序返回时,就会调用“错误”回调。

Take the function below. 使用以下功能。 There are some values (rangesize, dropskus, limit) (commented below) which are unique to each call, and defined in the success callback for each. 有些值(rangesize, dropskus, limit) (在下面注释)对于每个调用都是唯一的,并在每个成功回调中定义。 When I run the script and send 5 calls, if they do come back in a different order to which they were sent. 当我运行脚本并发送5个调用时,如果它们确实以与发送时不同的顺序返回。 Let's say the largest response was the 2nd call to be made, that response comes back last and also invokes the last callback function. 假设最大的响应是要进行的第二次调用,该响应最后返回,并且还调用最后一个回调函数。

So my question: 所以我的问题是:

Does or should JavaScript / jQuery know which callback function to invoke when returning multiple responses? 返回多个响应时,JavaScript / jQuery是否知道应该调用哪个回调函数?

Or would it be better for me to use synchronous calls? 还是对我来说使用同步呼叫会更好?

Other notes: When debugging in chrome, I noticed that the console logs of the responses say filename.js:linenumber . 其他说明:在chrome中调试时,我注意到响应的控制台日志显示为filename.js:linenumber Whereas previously when I've used multiple ajax calls the console logs say VM12*:linenumber . 以前,当我使用多个ajax调用时,控制台日志显示为VM12*:linenumber I don't know if this has anything to do with the issue I'm facing, but I did notice that when this was the case the correct callbacks were always invoked. 我不知道这是否与我面临的问题有关,但是我确实注意到,在这种情况下,总是会调用正确的回调。

function generateReview(){

  var subcategories = subcatstring.split(",");

  $("#rangereviewtable").html("");
  $("#rangereviewtable").append(thead);

  var i = 0;
  var iterations = subcategories.length;
  $.each(subcategories, function(key, value) {

        var postdata = {group: group,
                        class: rrclass,
                        category: category,
                        subcategory: value,
                        period: period,
                        periodval: periodval,
                        stores: storesarray};
        console.log(postdata);

        $.ajax({
                url: "ajaxrangereview.php",
                type: "post",
                dataType: 'json',
                data: postdata,
                success: function (response) {

                  //VALUES UNIQUE TO EACH CALL
                  var rangesize = parseInt($("#rangesize" + i).text());
                  console.log("range size: " + rangesize);
                  var dropskus = parseInt($("#dropskus" + i).text());
                  console.log("dropskus: " + dropskus);
                  var limit = rangesize - dropskus;
                  console.log("limit: " + limit);

                  console.log(response);

                  var rrtable = "";

                        $.each(response, function(i, item) {

                            rrtable += "<tr>";
                              rrtable += "<td class='rangereviewtext'>" + item.category + "</td>";
                              rrtable += "<td class='rangereviewtext'>" + item.subcategory + "</td>";
                              rrtable += "<td class='rangereviewtext'>" + item.brand + "</td>";
                              rrtable += "<td class='rangereviewtext'>" + item.sku + " - " + item.product + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.py3.toLocaleString("en") + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.py2.toLocaleString("en") + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.py1.toLocaleString("en") + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.average.toLocaleString("en") + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.sales.toLocaleString("en") + "</td>";
                              rrtable += "<td class='rangereviewnumber'>" + item.share + "%</td>";
                              rrtable += "<td>&#x2714</td>";

                              if(limit >= item.idnum){

                                  rrtable += "<td>&#x2714</td>";
                                  rrtable += "<td class='checkboxcell' onClick=\"toggleCheckMark(this, '" + item.brand + "')\">&#x2714</td>";

                              } else {

                                  rrtable += "<td></td>";
                                  rrtable += "<td class='checkboxcell' onClick=\"toggleCheckMark(this, '" + item.brand + "')\"></td>";

                              }

                            rrtable += "</tr>";
                        });
                    // increment iterations
                    i += 1;

                    $("#rangereviewtable").append(rrtable);

                    if(i == iterations){

                      var headimage = "<img src='http://url.com/images/oimage.png' width='63.5px' height='76px'>";
                      var table = $("#rangereviewtable").DataTable({
                                                                    buttons: [{extend: 'excelHtml5', title: 'Range Review', text: 'Export to Excel'},
                                                                              {extend: 'print', title: 'Range Review', message: headimage}],
                                                                  });
                      //insert export to excel button into #rrbuttons div above actual table.
                      table.buttons().container().appendTo( $('#rrbuttons') );

                      $("#rangereviewpanel").show();
                      $("#generatebutton").hide();
                      $("#loadbutton").hide();
                      $("#saveasbutton").show();
                      generateReviewSummary(summaryarray);

                    } else {

                      //do nothing not all iterations complete

                    }

                    },
                error: function(jqXHR, textStatus, errorThrown) {

                    i+= 1;
                    console.log(textStatus, errorThrown);
                  }
            });
  });
}

In your specific case I think it will work fine (your code does something like): 在您的特定情况下,我认为它可以正常工作(您的代码可以执行以下操作):

var j = 0;
for (var i = 0; i < 10; i++) {
    setTimeout(function () {
        console.log(j);
        j++
    }, 1000)
}

This code will outputs numbers 0..9; 该代码将输出数字0..9;
But let see another example: 但是让我们看另一个例子:

for (var i = 0; i < 10; i++) {
    setTimeout(function () {
        console.log(i);
    }, 1000)
}

It outputs 9..9 输出9..9
So it is not safe approach to call async functions inside synchronous loop. 因此在同步循环内调用异步函数是不安全的方法。 You can use async.each ( https://github.com/caolan/async#each ) or promises to handle it. 您可以使用async.each( https://github.com/caolan/async#each )或承诺对其进行处理。

JavaScript / jQuery knows exactly what callback to invoke, and is doing so correctly. JavaScript / jQuery确切知道要调用什么回调,并且正确地做到了。

You need to learn more about JavaScript closures and scope. 您需要了解有关JavaScript闭包和范围的更多信息。

I'm not sure what you mean by 'wrong' and 'right' callback with respect to your logic. 对于您的逻辑,我不确定您所说的“错误”和“正确”回调是什么意思。 If you want the special-logic to kick-in when you receive the response to the last request , then perhaps you could try replacing your use of i with references to key instead (or if I'm reading your logic correctly, you might want to use key + 1 ) 如果您希望在收到对最后一个请求的响应时启动特殊逻辑,那么也许可以尝试将对i的使用替换为对key引用(或者如果我正确阅读了逻辑,则可能需要使用key + 1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM