Jquery $ .when阻止代码执行和UI更新?

时间:2013-03-08 09:36:34

标签: javascript jquery

我们在单页面应用程序中使用JQuery。 我们的boostrapper是应用程序的起点,看起来像这样:

define('homebootstrapper',
    ['jquery', 'config', 'homerouteconfig', 'presenter', 'dataprimer', 'binder'],
    function ($, config, homeRouteConfig, presenter, dataprimer, binder) {
        var
            run = function () {
                $('#busyIndicator').activity(true);

                $.when(dataprimer.fetch())                
                    .done(function () {
                       // $('#busyIndicator').activity(false);
                    });
            };

        return {
            run: run
        };
    });

被调用的数据驱动程序如下所示:

define('dataprimer',
    ['ko', 'datacontext', 'config'],
    function (ko, datacontext, config) {

        var logger = config.logger,

            fetch = function () {

                return $.Deferred(function (def) {

                    console.log('in deferred');   
                    $.when(LongTimeProcessing())
                    .pipe(function () {                        
                        logger.success('Fetched data');
                    })

                    .fail(function () { def.reject(); })

                    .done(function () { def.resolve(); });

                }).promise();
            };

        return {
            fetch: fetch
        };
    });

function LongTimeProcessing(options) {
    console.log('in when');
    return $.Deferred(function (def) {
        var results = options;
        for (var i = 0; i < 10; i++) {
            var x = i;
            $('#counter').html(x);
        }
        def.resolve(results);
    }).promise();
}

$('#busyIndicator').activity(true);行 应该显示基于SVG或VML的进度动画。除了使用JQuery $ .when

之外,这种方法效果很好

使用此示例代码,我们尝试创建一个需要一些时间的方法,称为“LongTimeProcessing”(而不是通常通过放大使用的后端的ajax调用)

我们看到当我们使用jquery时,busyindicator不起作用(读取:不显示),直到dataprimer返回def.resolve()。这时似乎阻止了所有UI更新。此外,LongTimeProcessing方法的计数器值仅显示此循环的最后一个值。它被执行但它永远不可见。

我们做错了什么?我们该如何应对呢?

1 个答案:

答案 0 :(得分:3)

必须屈服于事件处理循环,以允许UI更新和处理未完成的事件。只有在您自己的代码执行完毕后才会发生这种情况。

您的LongTimeProcessing函数不会执行此操作,它会启动一个循环并且不会将控制权返回给浏览器,直到该循环结束。

您可以使用setTimeout来处理循环迭代,从而实现您想要的目标:

function LongTimeProcessing(options) {
    console.log('in when');
    return $.Deferred(function (def) {
        var results = options;

        var i = 0;

        (function iterate() {
            $('#counter').html(i);
            if (i < 10) {
                ++i;
                setTimeout(iterate, 250);
            } else {
                def.resolve(results);
            }
        })();

    }).promise();
}

setTimeout的调用允许函数在第一次调用iterate()后立即终止,允许浏览器的事件处理循环处理UI更新,然后进入下一次迭代当计时器过去时。