我在每个循环的jQuery中进行“重”画布操作,导致较慢的设备(IE和iPad)有时变得完全没有响应。
所以我想我可以使用下划线的_.defer()
来排列每个循环中的函数,如:
function handleAsset = _.defer(function(){
//weightlifting goes here (partly async)
});
$.each(assets, handleAsset);
然而,这引发了一个奇怪的错误(堆栈跟踪指向$.each
):
Uncaught TypeError: Object 20877 has no method 'call'
这种方法有缺陷吗?这是由于处理程序函数内部的异步操作吗?是否有另一种/更好的方法来实现这一目标?
答案 0 :(得分:22)
它有缺陷。您应该尽可能在最低点尝试解耦 / 分解代码。我认为从长远来看,仅仅解耦循环的每次迭代是不够的。
但是,您真正需要做的是,设置一个异步失控定时器,它为实现提供足够的空间来更新 UI Queue (或 UI螺纹)。这通常使用setTimeout()
(客户端),nextTick
(node.js)或setImmediate
(即将推出)等方法完成。
例如,假设我们有一个数组,我们想要处理每个条目
var data = new Array(10000).join( 'data-' ).split('-'); // create 10.000 entries
function process( elem ) {
// assume heavy operations
elem.charAt(1) + elem.charAt(2);
}
for(var i = 0, len = data.length; i < len; i++ ) {
process( data[i] );
}
现在这段代码是一个经典循环,遍历数组并处理其数据。它还会消耗100%的CPU时间,因此只要处理所有条目就会阻止浏览器 UI队列(这基本上意味着,浏览器UI将冻结而且没有反应。)
为了避免这种情况,我们可以创建一个这样的结构:
var data = new Array(10000).join( 'data-' ).split('-'); // create 10.000 entries
function runAsync( data ) {
var start = Date.now();
do {
process( data.shift() );
} while( data.length && Date.now() - start > 100 );
if( data.length ) {
setTimeout( runAsync.bind( null, data ), 100 );
}
}
runAsync( data.concat() );
这里会发生什么?
我们基本上做的是:
setTimeout
)并为UI提供更新的机会100 ms 以上的任何延迟通常被人眼识别为“ lag ”。下面的任何东西看起来都很流利和好看(至少我们的眼睛会告诉我们)。 100ms是一个很好的值,作为最大处理时间的限制。我甚至建议下降到50ms。
这里需要注意的是整体处理时间会增加,但我认为更长时间处理并保持响应更快,更快的处理速度和非常糟糕的用户体验。
快速演示:
答案 1 :(得分:2)
所以你想限制并发异步操作的数量?您的实施中的缺陷是您将推迟每个操作,直到上一个操作完成。
一个选项是使用序列助手,然后可以将此队列分解为更易于管理的块进行处理。
https://github.com/michiel/asynchelper-js/blob/master/lib/sequencer.js
var actions = [];
$.each(assets, function(key, value) {
actions.push(function(callback) {
$.ajax({
url: 'process.php?id='+val,
success: function(msg) {
callback();
}
});
});
}
);
var sequencer = new Sequencer(actions);
sequencer.start();
如果将actions数组拆分为两个数组,并让它们并排运行,那么一次只能运行两个进程,直到两个队列都完成为止。
e.g。
var arr1 = actions.splice(0,100);
var arr2 = actions.splice(100,200);
var sequencer1 = new Sequencer(arr1);
sequencer1.start();
var sequencer2 = new Sequencer(arr2);
sequencer2.start();