【问题标题】:ajax function in for loop not updating DOM even with setTimeout即使使用 setTimeout,for 循环中的 ajax 函数也不会更新 DOM
【发布时间】:2018-11-16 11:49:36
【问题描述】:

我正在编写一个函数来使用 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);
   }
}

抱歉,拖了这么久,提前谢谢。

【问题讨论】:

  • 我觉得是因为你使用了同步调用

标签: javascript jquery html ajax dom


【解决方案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);
}

【讨论】:

    【解决方案2】:

    首先,我们需要修复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() 被调用后约 20 毫秒。

    让我们看看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


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

    • 假设服务器响应第一个请求需要 100 毫秒,第二个请求需要 10 毫秒。即使有 20 毫秒的延迟,第二个请求也将首先执行。而仅仅增加延迟并不是解决方案,因为:
    • 如果您的服务器对延迟的响应速度更快,那么您就是在给用户带来不必要的等待。

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

    【讨论】:

    • setTimeout 调用是错误的顺便说一句,您调用的是函数而不是将其作为参数传递。
    猜你喜欢
    • 2017-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多