前几天我注意到这里有一个问题(减少 Javascript CPU 使用率 https://stackoverflow.com/questions/6846681/reducing-javascript-cpu-usage)我很感兴趣。
本质上,这个人想要逐个字符地加密一些文件。显然,一次性完成所有这些操作将会锁定浏览器。
他的第一个想法是一次以大约 1kb 的字符串块的形式进行处理,然后暂停 X 毫秒,这样用户就可以在处理之间保持与页面的交互。他还考虑过使用 webWorkers (最好的想法),但它显然不是跨浏览器的。
现在我真的不想解释为什么这在 javascript 中可能不是一个好主意。但我想看看是否能想出一个解决方案。
我记得看过 Douglas Crockford 的视频在 jsconf http://blip.tv/jsconfeu/douglas-crockford-loopage-4384069。该视频与 Node.js 和事件循环相关。但我记得他谈到将长时间运行的函数分解为单独的块,因此新调用的函数会到达事件循环的末尾。而不是用长时间运行的任务阻塞事件循环,从而防止其他事情发生。
我知道这是一个值得我研究的解决方案。作为一名前端开发人员,我从未真正经历过 JS 中运行时间极长的任务,并且热衷于了解如何分解它们以及它们如何执行。
我决定尝试一个递归函数,它从 0 毫秒的 setTimeout 内部调用自身。我认为这将为事件循环中想要在运行时发生的任何其他事情提供中断。但我也认为,当没有其他事情发生时,你将获得最大的计算量。
这是我想出的。
(我要为代码道歉。我正在控制台中进行实验,所以这又快又脏。)
function test(i, ar, callback, start){
if ( ar === undefined ){
var ar = [],
start = new Date;
};
if ( ar.length < i ){
ar.push( i - ( i - ar.length ) );
setTimeout(function(){
test( i, ar, callback, start);
},0);
}
else {
callback(ar, start);
};
}
(您可以将此代码粘贴到控制台中,它将起作用)
本质上,该函数的作用是获取一个数字,创建一个数组并在调用自身时调用自身array.length < number
将到目前为止的计数推入数组。它将第一次调用中创建的数组传递给所有后续调用。
我对其进行了测试,它似乎完全按照预期工作。只是它的性能相当差。我用它测试了一下..
(这又不是性感的代码)
test(5000, undefined, function(ar, start ){
var finish = new Date;
console.log(
ar.length,
'timeTaken: ', finish - start
);
});
现在我显然想知道需要多长时间才能完成,上面的代码大约花了20秒。现在在我看来,JS 计数到 5000 不应该需要 20 秒。再加上它正在做一些计算和处理,以将项目推送到数组中。但 20 多岁还是有点陡。
因此,我决定同时生成几个,看看这对浏览器性能和计算速度有何影响。
(代码并没有变得更性感)
function foo(){
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 1' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 2' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 3' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 4' ) });
test(5000, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 5' ) });
};
所以总共有五个,同时运行并且不会导致浏览器挂起。
进程结束后,所有结果几乎同时返回。全部完成大约需要 21.5 秒。这仅比单独运行慢 1.5 秒。但我将鼠标在窗口中移动到具有以下内容的元素上::hover
效果只是为了确保浏览器仍然响应,因此这可能会导致 1.5 秒的开销。
因此,由于这些函数显然是并行运行的,因此浏览器中留下了更多的计算资源。
有谁能够解释一下这里发生的性能问题,并详细说明如何改进这样的功能?
只是为了发疯我这样做了..
function foo(){
var count = 100000000000000000000000000000000000000;
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 1' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 2' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 3' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 4' ) });
test(count, undefined, function(ar, start ){ var finish = new Date; console.log(ar.length, 'timeTaken: ', finish - start, 'issue: 5' ) });
};
在我写这篇文章的整个过程中,它一直在运行,并且仍在继续。浏览器没有抱怨或挂起。结束后我会添加完成时间。