我在 jQuery 中执行“繁重”的 Canvas 操作,每个循环导致较慢的设备(IE 和 iPad)有时变得完全没有响应。
所以我想我可以使用下划线的 _.defer()
来对每个循环中的函数进行排队,例如:
function handleAsset = _.defer(function(){
//weightlifting goes here (partly async)
});
$.each(assets, handleAsset);
但这会引发一个奇怪的错误(堆栈跟踪指向 $.each
):
Uncaught TypeError: Object 20877 has no method 'call'
这种方法有缺陷吗?这是由于处理函数内部正在进行异步操作吗?是否有另一种/更好的方法来实现这一目标?
最佳答案
这是有缺陷的。您应该尝试在可能的最低点解耦/分解 代码。我认为从长远来看,仅仅解耦循环的每次迭代不太可能就足够了。
但是,您真正需要做的是设置一个异步失控计时器,它为实现提供了足够的空间来更新UI 队列(或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 将卡住变得 react 迟钝)。
为了避免这种情况,我们可以创建一个这样的结构:
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() );
这里发生了什么?
我们基本上做的是:
- 获取数组并在 100 毫秒 的时间范围内处理尽可能多的数据/条目
- 之后,停止处理(调用
setTimeout
)并给UI一个更新的机会 - 只要数组中还有数据就这样做
任何超过 100 毫秒 的延迟通常被人眼识别为“滞后”。下面的任何内容看起来都很流畅和漂亮(至少我们的眼睛会告诉我们)。 100 毫秒作为最大处理时间的限制是一个很好的值。我什至建议减少到 50 毫秒。
这里需要注意的是,整体处理时间会增加,但我认为处理时间更长并保持响应速度更好,而不是处理速度更快和非常糟糕的用户体验。
快速演示:
关于javascript - 我怎样才能推迟每个循环的jQuery,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13955976/