手头的一个项目,根据客户的要求,在前端自动生成大量的iframe,比如90个iframe,而且这些iframe是嵌套在一个大的iframe下的,不要问我为什么这样。
方案1:将动态生成的url,附加到iframe的src中,比如后端生成了90个iframe,那么将这些数据返回到浏览器,然后让浏览器自动去请求。
这个时候的现象是:浏览器直接假死,就是任何操作都已经做不了了。
猜想原因是:当文档加载完成时,将会去自动发起90个iframe的请求,这个太消耗浏览器资源了,当时我依稀记得chrome可以同时发起4个连接,因此,我猜想可能是由于多个线程去争夺连接资源,导致效率太低,因此,必须限制连接数,因此,发起请求的操作必须要有js来控制。
方案2:将动态生成的url保存在页面的某个位置,然后生成的iframe的src为空(没有连接请求)。然后在大的iframe加载完毕之后,调用如下函数进行加载:
//onload
var count = $("#div iframe").size();//iframe的个数
updateIframeSrc(0,count);
updateIframeSrc(1,count);
updateIframeSrc(2,count);
function updateIframeSrc(index,count){
if(index >= count)
return;
var iframe = $("#rawdiv iframe").eq(index);
iframe.load(function(){
console.log(index+"complete");
updateIframeSrc(index+3,count);
});
var url = "......";
iframe.attr(src,url);
}
我的想法数如果能充分利用浏览器最大连接数为4的特性,使用其中3个去并发操作,然后这样就可以充分利用网络资源了,同时又不至于太卡顿。但是我没找到jquery发起并发的方法,于是只能自作聪明的写了一段类似于递归的方法,当上个文档加载完成时在去加载下一个。
结果:仍然是存在卡顿现象,而且其中有些iframe中的内容无法显示完整文档,但是这个链接时正确的,把两个html拿出来比对了一下,发现iframe中请求的document缺少了一部分,而且是中间缺少了一部分,这个是什么原因,我现在也不清楚,,,,
紧接着,我调研了一下js如何开启多线程。结果答案非常失望:js引擎只能是单线程的,浏览器核心有多个线程,包括但不仅限于连接线程(默认4个),gui渲染线程,js线程。由于js单线程的特性,导致实现起来比较简单,不需要考虑同步问题。与此同时,文章里面描述了js引擎对事件处理的机制。
js是种靠事件驱动的机制,有点像消费者和生产者一样。浏览器核心不停的像一个队列添加事件,比如浏览器点击事件,定时时间等等,而js处理是每次都去取队列头中的元素(如果有的话),然后调用响应函数,并且并且,当这个元素没处理完之前是不会处理下一个的。这也就解释了为什么会出现卡顿,因为鼠标的动作发到js引擎时,前面有太多事件要操作,导致鼠标事件无法处理。
方案3:加载iframe的时间间隔错开,说白了,给js引擎留出处理其他事件的时间。于是有了下面代码
for(var i = 0;i<count;i++){
setTimeout(function(){
var iframe = $("#rawdiv iframe").eq(i);
iframe.attr("src",iframe.attr("id"));
},i*1000);
}
意思时当文档加载完成时,添加一系列的定时事件。即定时更改iframe的src。
但是但是!!!do not work!!! 我调试了一下,在setTimeout函数内部获取到的i值总是最后一个值,原因不明。
既然如此,那使用setInterval会如何呢?即有了如下代码
var index = 0;
var timer = setInterval(function(){
if(index >= count){
clearInterval(timer);
return;
}
updateIframeSrc(index++)},500);
OK!可以正常work了。
方案4:于是我又尝试了一下最开始的做法,将代码变成如下,同样可以work!
//main
setTimeout(updateIframeSrc(0),500);
//可以添加一些限制,比如index == count时就返回。不过这里没有判断也可以,因为下次事件的加载是在本次加载完成之后才设置的。而setinterval 则一直向js引擎中添加事件,因此,必须及时取消。
function updateIframeSrc(index){
var iframe = $("#rawdiv iframe").eq(index);
iframe.attr("src",iframe.attr("id"));
iframe.load(function(){
setTimeout(function(){
updateIframeSrc(++index);
},500);
});
}
这段代码就是当上个iframe加载完成之后,再设定一个新的定时去加载,间隔500毫秒,用于处理一些其他操作。
上面两种方法,个人更青睐于第二种,因为每个iframe加载时间不相同,将加载间隔设死,当上个iframe加载时间太长时,将会导致马上处理下个iframe的加载,会存在一点点卡顿。
参考链接:这里写链接内容