我们在单页应用程序中使用 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 不工作(read: 不显示),直到 dataprimer 返回 def.resolve()。这似乎会阻止所有 UI 更新。此外,LongTimeProcessing 方法的计数器值仅显示此循环的最后一个值。它已执行但永远不可见。
我们做错了什么?我们应该如何处理。
最佳答案
您必须屈服于事件处理循环,以允许 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 更新,然后继续当计时器结束时进入下一次迭代。
关于javascript - Jquery $.when 阻止代码执行和 UI 更新?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15290817/