如何在AJAX / Javascript中调用长时间运行的进程时更新UI?

时间:2012-11-09 20:22:56

标签: javascript ajax

我有多个需要串行调用的进程(我正在使用同步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运行3Done 3Done 3Done 3

我想得到

运行1Done 1运行2Done 2运行3Done 3

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

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

3 个答案:

答案 0 :(得分:2)

听起来你想要排队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

答案 1 :(得分:0)

您在浏览器中必须了解的有关Javascript的一点是它是单线程的。当您更改DOM时,屏幕不会更改,直到您的脚本放弃控制权为止。

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

  1. 致电doWork()
  2. doWork()添加了正在运行的消息
  3. 将对updateStatus()的调用排队以稍后执行
  4. 再次致电doWork()
  5. doWork()添加了正在运行的消息
  6. 将对updateStatus()的调用排队以稍后执行
  7. runProcess()退出并将控制权返回给浏览器
  8. 浏览器使用正在运行的消息更新屏幕
  9. 第一次updateStatus()来电。完成后,屏幕会更新
  10. 第二次updateStatus()来电。完成后,屏幕会更新
  11. 您可以根本不弄乱updateStatus()来电,从而获得您想要的内容,并在doWork()内打印完成的消息。

答案 2 :(得分:0)

队列效果很好。在需要串联调用的$ .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>