即使使用setTimeout,for循环中的ajax函数也不会更新DOM

时间:2018-06-06 17:36:36

标签: javascript jquery html ajax dom

我正在编写一个函数,在页面加载时使用ajax从后端服​​务器获取指令。我的ajax代码根据数字获取指令,并使用jquery和控制台使用此<p id="loadText"></p>元素中的变量response.setText打印指令。这是我的ajax功能:

function ajaxFetch(s) {
    var success = false;
    $.ajax({
        type: "POST",
        url: "post.php",
        data: {
            step: s
        },
        async: false,
        dataType: 'JSON',
        success: function (response) {
            $("#loadText").text(response.stepText);
            console.log(response.stepText);
            success = true;
        }
    });
    return success;
}

我正在尝试使用另一个函数来循环执行步骤,无论有多少,但这是我遇到的问题:

  • ajaxFetch()直到上次执行才更新DOM
  • 尝试了setTimeout()而没有更新DOM
  • for循环遍历ajaxFetch()太快
  • response.stepText按时在控制台中打印,但不会按时更新DOM

以下是我尝试过的示例循环:

function uploadSteps(maxStep) {
   for (var x = 1; x <= maxStep; x++){
       setTimeout(ajaxFetch(x), 20);
   }
}

对不起,这太长了,并提前感谢。

2 个答案:

答案 0 :(得分:1)

首先,我们需要修复uploadSteps函数中的错误:

function uploadSteps(maxStep) {
   // here change `var x` to `let x` to avoid problems 
   // like here - https://stackoverflow.com/q/750486/5811984
   for (let x = 1; x <= maxStep; x++){
       setTimeout(function() {
           // notice how here ajaxFetch(x) is wrapped into a function, 
           // otherwise it gets called right away
           ajaxFetch(x)
       }, 20);
   }
} 

现在这是另一个问题 - 所有的setTimeout都将以20ms延迟调用,这意味着它们将同时执行,但在调用uploadSteps()后约20ms。

让我们看看maxStep=3时会发生什么(假设您的CPU非常快,因为这与理解问题无关):

Time passed | what happens
--------------------------
0ms         | setTimeout(ajaxFetch(1), 20) is called
0ms         | setTimeout(ajaxFetch(2), 20) is called
0ms         | setTimeout(ajaxFetch(3), 20) is called
20ms        | ajaxFetch(1) is called
20ms        | ajaxFetch(2) is called
20ms        | ajaxFetch(3) is called

因此,当您看到所有ajaxFetch被同时调用时,我认为这不是您所需要的。您可能正在寻找的是:

Time passed | what happens
--------------------------
0ms         | setTimeout(ajaxFetch(1), 20) is called
0ms         | setTimeout(ajaxFetch(2), 40) is called
0ms         | setTimeout(ajaxFetch(3), 60) is called
20ms        | ajaxFetch(1) is called
40ms        | ajaxFetch(2) is called
60ms        | ajaxFetch(3) is called

只需稍微更改代码即可实现

function uploadSteps(maxStep) {
   for (let x = 1; x <= maxStep; x++){
       setTimeout(function() {
           ajaxFetch(x)
       }, 20 * x); // change delay from 20 -> 20 * x
   }
} 

此外,您似乎不需要从ajaxFetch()返回任何内容,因此最好将其设置为异步,以便它不会阻止代码执行:

function ajaxFetch(s) {
    $.ajax({
        type: "POST",
        url: "post.php",
        data: {
            step: s
        },
        // async: false, -- remove this, it's true by default
        dataType: 'JSON',
        success: function (response) {
            $("#loadText").text(response.stepText);
            console.log(response.stepText);
        }
    });
}

即使你实际需要为fetchAjax()返回一些东西,最好保持异步并使用回调/承诺。 jQuery实际上强烈反对在任何情况下使用async: false

如果您添加setTimeout的原因是为了确保按顺序执行所有步骤,那么这不是正确的方法。问题是:

  • 假设服务器响应第一个请求需要100毫秒,第二个请求需要10毫秒。即使延迟20ms,第二个请求也将首先执行。只是增加延迟不是解决方案,因为:
  • 如果您的服务器响应延迟的速度要快得多,则会为用户带来不必要的等待。

最好添加一个来自ajaxFetch()的回调,该回调将在ajax抓取完成时调用,然后在收到回调后调用下一个ajaxFetch()

答案 1 :(得分:1)

当你的for循环完成时,比方说20次迭代,ajaxFetch中的ajax调用只会收到前几次调用的响应,你最后看到的是最后一次ajax调用的响应。您可以使用此链接了解异步调用在javascript中的工作方式 https://rowanmanning.com/posts/javascript-for-beginners-async/

所以答案是,你需要等到第一个ajax调用完成后再以20ms的超时时间再次调用该方法,就像这样

var globalMaxSteps = 1;
var startIndex = 1;

function ajaxFetch(s) {
    $.ajax({
        type: "POST",
        url: "post.php",
        data: {
            step: s
        },
        async: false,
        dataType: 'JSON',
        success: function (response) {
            $("#loadText").text(response.stepText);
            console.log(response.stepText);
            startIndex++;
            if(startIndex <= globalMaxSteps) {
           setTimeout(function(){
           ajaxFetch((startIndex); 
               },20);
        } else {
               console.log("All Iterations complete");
        }

        }
    });
}


function uploadSteps(maxStep) {
   startIndex = 1;
   globalMaxSteps = maxStep;
   setTimeout(function(){
    ajaxFetch(startIndex); 
   },20);
}