本文探讨JavaScript单线程环境下耗时操作导致的浏览器假死问题,重点介绍利用setTimeout进行数组分块处理的优化方案。通过对比常规循环与setTimeout延时调用的执行效果,展示如何在保证主线程响应的同时高效处理大量数据,提升用户体验。
JavaScript单线程阻塞问题与异步思路
此前曾介绍过如何使用setTimeout替代setInterval进行间歇调用,本期将继续深入《JavaScript高级程序设计》中关于setTimeout的另一种应用场景——防止循环超时。
【这是铺垫,为故事的高潮埋下伏笔】
JavaScript是单线程语言,代码块内的指令必须按顺序从上至下依次执行。若中间存在耗时较长的代码段,后续代码将无法执行,从而导致浏览器出现假死状态。
JavaScript中常见的耗时操作主要有两类:一是向服务器发起请求;二是对数组进行循环操作。此外,还有一种常见情况是将两者结合,即在循环中向服务器发送请求,这在实际项目中屡见不鲜。
解决上述耗时问题的思路一致,即采用异步编程。需要注意的是,JavaScript的异步并非多线程,因为其本质是单线程。从直观上看,JavaScript的异步体现为延时和回调机制。
针对第一类耗时情况,通常采用Ajax异步请求,待请求返回结果后执行回调操作。
针对第二类耗时情况,本文即将介绍的方法是利用setTimeout进行延时调用,从而实现数组的分块处理。
【这才是高潮】
假设我们需要处理一个大小为100的数组,对数组中的每个元素执行大量处理逻辑,每个元素的处理时间约为1秒。
同时假设程序后续代码不依赖于对该数组的处理结果。
基于上述条件,我们采用两种方式来处理该数组:一种是常规方式,另一种是利用setTimeout进行数组分块处理。
利用setTimeout实现数组分块处理
var processTime = 0;
//常规操作
tcCircle();
//注释上面的代码 放开下面注释 以执行setTimeout数组分块操作
//tcCircleUseSetTimeout();
//time consuming circle
function tcCircle(){
var arr = new Array(100);
for(var i=0;i<arr.length;i++){
process(arr[i]);
}
//页面标题栏一直转圈 且下面的语句迟迟无法执行
console.log("important process");
console.log("finish!");
}
function tcCircleUseSetTimeout(){
var arr = new Array(100);
setTimeout(function(){
var ele = arr.shift();
process(ele);
if(arr.length>0){
setTimeout(arguments.callee,100);
}
},100);
console.log("important process");
console.log("finish!");
}
function process(ele){
console.log("process"+(++processTime));
//模拟长时间的处理过程
sleep(1000);
}
function sleep(sleepTime){
var start=new Date().getTime();
while(true){
if(new Date().getTime()-start>sleepTime){
break;
}
}
}
首先执行常规操作。由于JavaScript是单线程的,可以预见,执行完这段程序至少需要1*100=100秒,且浏览器会出现假死现象。

接着执行基于setTimeout的方法。该方法每次仅操作数组中的一个对象,并在每次操作之间设置100毫秒的延时,供JavaScript引擎执行主线程代码。因此,执行效果显著优化。

结果显示,浏览器标题栏不再持续旋转加载,控制台也迅速打印出代码末尾的语句,表明主程序已执行完毕,而数组处理则在定时器中逐步执行。
通过此类优化,程序的用户体验得到显著提升。当然,这仅是书中介绍的一种方法,相信还有其他实现方式,例如Promise。如有其他更好的处理循环耗时操作的方法,欢迎交流补充。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持本站!