【问题标题】:How do I update UI while calling a long running process in AJAX/Javascript?如何在 AJAX/Javascript 中调用长时间运行的进程时更新 UI?
【发布时间】:2012-11-09 20:22:55
【问题描述】:

我有多个需要串联调用的进程(我正在使用同步 AJAX 调用)。我想在完成后显示一个长时间运行的进程的状态,然后继续下一个。这是我编写的示例代码。

    var counter = 0;
function runProcess(input, output) {
    doWork();
    doWork();
    doWork();
};
function doWork() {
    counter++;
    document.getElementById('<%=this.progressMonitor.ClientID%>').innerHTML += "Running " + counter;

    // some lengthy calculations
    for (var i = 0; i < 1000000; i++) {
        var foo = Math.random();
    }
    setTimeout(updateStatus, 1000);
};
function updateStatus() {
    document.getElementById('<%=this.progressMonitor.ClientID%>').innerHTML += "Done " + counter;
}

当我运行它时,我得到以下响应:

跑步1跑步2跑步3完成3完成3完成3

我想得到

运行 1 完成 1 运行 2 完成 2 运行 3 完成 3

如果我在 updateStatus 函数中插入一个 alert() 语句,那么我会得到我想要的响应/执行顺序。浏览器是否为 3 个函数调用创建 3 个线程并异步执行它们?

如何连续运行? setTimeout 是否正确实施?提前感谢您的帮助。

【问题讨论】:

  • 这里的 Ajax 调用如何显示?他们会去你样本中的数学循环吗?
  • 感谢您的帮助。我刚刚注意到你之前的帖子。为了澄清,这是一个示例代码。在现实世界中,doWork 将被替换为 CallService 函数,该函数将同步调用 WCF 服务。

标签: javascript ajax


【解决方案1】:

听起来您想将 AJAX 调用排队。 jQuery 提供了一个很好的队列函数,因此您可以确保一系列函数一个接一个地被调用。 Mootools 和其他框架提供了类似的功能。

这是我的 fiddle 中的代码,它显示了如何做到这一点。

var queueElement = $("#output");
var counter = 0;

function doWork(next) {
    queueElement.append("Starting " + counter + "<br/>");
    counter++;
    $.ajax('/echo/json/').always(function() {
        queueElement.append("Ending " + counter + "<br/>");
        next();
    });
}

queueElement.queue('workQueue', doWork);
queueElement.queue('workQueue', doWork);
queueElement.queue('workQueue', doWork);
queueElement.dequeue('workQueue'); // start the work queue

【讨论】:

    【解决方案2】:

    关于浏览器中的 Javascript,您必须了解的一件事是它是单线程的。当您更改 DOM 时,屏幕不会改变,直到您的脚本放弃控制。

    以下是调用 doWork() 两次时的真实事件顺序:

    1. 致电doWork()
    2. doWork()添加运行消息
    3. 排队调用updateStatus() 以便稍后执行
    4. 再次致电doWork()
    5. doWork() 添加运行消息
    6. 排队调用updateStatus() 以便稍后执行
    7. runProcess() 退出并将控制权返回给浏览器
    8. 浏览器使用正在运行的消息更新屏幕
    9. 第一次调用updateStatus()。完成后,屏幕更新
    10. 第二个updateStatus() 调用已完成。完成后,屏幕更新

    您可以通过完全不与updateStatus() 调用来获得您想要的东西,只需从doWork() 内部打印您完成的消息。

    【讨论】:

    • 谢谢。鉴于您的解释,它仍然是单线程的,这让我感觉更好。我遇到的问题是浏览器执行所有内容然后发送响应(所有内容一起)。我想在每项服务完成后立即通知用户。我认为 setTimeout 会给浏览器足够的时间来显示消息,但它似乎不起作用。我尝试了许多代码示例,但无法弄清楚这一点。我在 Windows 7 上使用 IE9。再次感谢。
    • 有一种方法可以对 ajax 调用进行排队,以便它们一个接一个地执行。我将制作一个示例并将其发布在新答案中。
    【解决方案3】:

    队列效果很好。在需要连续调用的 $.ajax 方法上使用队列效果很好。如果我在不使用队列的情况下使用“async: false”选项,则 UI 只会在所有长时间运行的进程完成后才会刷新。

    感谢您为我指明正确的方向!这是我的最终代码,以防有人查看:

    function callService(url, workflowid, modelid, next) {
            var jData = {};
            jData.workflowid = workflowid;
            jData.modelid = modelid;
            //alert(JSON.stringify(jData));
            $.ajax({
                url: url,
                type: "POST",
                dataType: "json",
                contentType: "application/json; charset=utf-8;",
                async: true,
                cache: false,
                data: JSON.stringify(jData),
                success: function (oResult) {
                    $("#Message").append("&nbsp;&nbsp;" + oResult.Message);
                    if (!oResult.Success) {
                        $("#<%:this.FailureText.ClientID %>").append("&nbsp;&nbsp;" + oResult.Error);
                    } else { next(); }
                },
                error: function (exc) {
                    $("#Message").append("&nbsp;&nbsp;" + exc.responseText);
                }
            });
        }
        function runServices() {
            var queueElement = $("#q");
            var queueStatus = $("#Message");
    
            queueStatus.append("<br/>Starting workflows at : " + new Date() + "<br/>");
            var list = $(":input:checkbox:checked"); // getting all selected checkboxes.
            $(list.each(function () {
                var workflowid = $(this).val();
                queueElement.queue("WorkflowQ", function (next) {
                    callService("/Services/Workflow.svc/rest/runworkflow", workflowid, document.getElementById('<%=this.MODELID.ClientID%>').value, next);
                });
            }));          
            queueElement.dequeue("WorkflowQ");
        };
    
        <input type="button" id="ExecuteButton" value="Execute Workflow" class="button" onclick="runServices();"/>
    
    <div id="Message"></div>
    <div id="q"></div>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-14
      • 2020-06-20
      • 1970-01-01
      • 1970-01-01
      • 2015-04-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多