1

我们在单页应用程序中使用 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
        };
    });

被调用的 dataprimer 如下所示:

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 方法中的计数器值仅显示此循环中的最后一个值。它被执行但它永远不可见。

我们做错了什么?我们应该如何处理。

4

1 回答 1

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 更新,然后在计时器结束时进入下一次迭代。

于 2013-03-08T09:41:10.913 回答