当您需要在客户端进行大量处理时,您需要将工作拆分为单独的线程。浏览器只有一个线程来处理用户输入(事件)和处理 JS。如果你处理了太多的 JS 而没有让步,那么 UI 就会变得无响应并且浏览器会不高兴。
如何让你的脚本屈服?新方法是使用网络工作者 http://www.whatwg.org/specs/web-workers/current-work/ 。这通过创建一个单独的线程来运行你的 JS 来工作,线程线程不访问 DOM 并且可以并发运行。
但是,并非所有浏览器都存在这种新技术。对于较旧的浏览器,您可以通过让脚本通过超时调用自身来拆分您的工作。每当发生超时时,脚本都会让浏览器运行其事件,一旦浏览器完成,您的下一个超时将被触发。
示例 http://jsfiddle.net/mendesjuan/PucXf/
var list = [];
for (var i = 0; i < 500000; i++) {
list.push(Math.random());
}
function sumOfSquares(list) {
var total = 0;
for (var i = 0; i < list.length; i++) {
total += list[i] * list[i];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Sync temp value = " + total;
document.body.appendChild(node);
}
return total;
}
function sumOfSquaresAsync(arr, callback) {
var chunkSize = 1000; // Do 1000 at a time
var arrLen = arr.length;
var index = 0;
var total = 0;
nextStep();
function nextStep() {
var step = 0;
while (step < chunkSize && index < arrLen) {
total += arr[index] * arr[index];
// DOM manipulation to make it take longer
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
index++;
step++;
}
if (index < arrLen) {
setTimeout(nextStep, 10);
} else {
callback(total);
}
}
}
sumOfSquaresAsync(list, function(total) {console.log("Async Result: " + total)});
//console.log("Sync result" + sumOfSquares(list));
jsfiddle上的例子把同步调用注释掉了,你可以把它放回去看看浏览器爬起来了。请注意,异步调用确实需要很长时间才能完成,但它不会导致长时间运行的脚本消息,它允许您在计算时与页面交互(选择文本、按钮悬停效果)。您可以看到它在右下角的窗格中打印部分结果。
更新 http://jsfiddle.net/mendesjuan/PucXf/8/
让我们尝试使用 c-smile 的任务函数来实现平方和。我认为他缺少一个参数,一个在任务完成时回调的函数。使用task 允许我们创建多个分块函数,而无需重复调用 setTimeout 和迭代的工作。
/**
* @param {function} worker. It is passed two values, the current array index,
* and the item at that index
* @param {array} list Items to be traversed
* @param {callback} The function to call when iteration is finished;
* @param {number} maxit The number of iterations of the loop to run
* before yielding, defaults to 1000
*/
function task(worker, list, callback, maxit)
{
maxit = maxit || 1000;
var idx = 0;
exec_chunk();
function exec_chunk()
{
for(var n = 0; n < maxit; ++n)
{
if(idx >= list.length) {
callback();
return;
}
worker(idx, list[idx]);
idx++;
}
setTimeout(exec_chunk,1);
}
}
function sumOfSquaresAsync(list, callback)
{
var total = 0;
// The function that does the adding and squaring
function squareAndAdd(index, item) {
total += item * item;
// DOM manipulation to make it take longer and to see progress
var node = document.createElement("div");
node.innerHTML = "Async temp value = " + total;
document.body.appendChild(node);
}
// Let the caller know what the result is when iteration is finished
function onFinish() {
callback(total);
}
task(squareAndAdd, list, onFinish);
}
var list = [];
for (var i = 0; i < 100000; i++) {
list.push(Math.random());
}
sumOfSquaresAsync(list, function(total) {
console.log("Sum of Squares is " + total);
})