js 执行一个耗时操作时,比如一个大的遍历,如何才能真正实现异步,让浏览器界面不会假死。

作者&投稿:贲婵 (若有异议请与网页底部的电邮联系)
js异步问题怎么解决~

异步加载又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有三种方式。
方法一:也叫Script DOM Element
(function(){
var scriptEle = document.createElement("script");
scriptEle.type = "text/javasctipt";
scriptEle.async = true;
scriptEle.src = "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js";
var x = document.getElementsByTagName("head")[0];
x.insertBefore(scriptEle, x.firstChild);
})();

属性是HTML5中新增的异步支持。此方法被称为Script DOM Element 方法。Google Analytics 和 Google+ Badge 都使用了这种异步加载代码
(function(){;
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
})();

但是这种加载方式执行完之前会阻止onload事件的触发,而现在很多页面的代码都在onload时还执行额外的渲染工作,所以还是会阻塞部分页面的初始化处理。
方法二:onload时的异步加载
(function(){
if(window.attachEvent){
window.attachEvent("load", asyncLoad);
}else{
window.addEventListener("load", asyncLoad);
}
var asyncLoad = function(){
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
}
)();

这种方法只是把插入script的方法放在一个函数里面,然后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题。
注:DOMContentLoaded与load的区别。前者是在document已经解析完成,页面中的dom元素可用,但是页面中的图片,视频,音频等资源未加载完,作用同jQuery中的ready事件;后者的区别在于页面所有资源全部加载完毕。
方法三:其他方法
由于JavaScript的动态性,还有很多异步加载方法: XHR Injection、 XHR Eval、 Script In Iframe、 Script defer属性、 document.write(script tag)。
XHR Injection(XHR 注入):通过XMLHttpRequest来获取javascript,然后创建一个script元素插入到DOM结构中。ajax请求成功后设置script.text为请求成功后返回的responseText。
//获取XMLHttpRequest对象,考虑兼容性。
var getXmlHttp = function(){
var obj;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
};
//采用Http请求get方式;open()方法的第三个参数表示采用异步(true)还是同步(false)处理
var xmlHttp = getXmlHttp();
xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
xmlHttp.send();
xmlHttp.onreadystatechange = function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
var script = document.createElement("script");
script.text = xmlHttp.responseText;
document.getElementsByTagName("head")[0].appendChild(script);
}
}

XHR Eval:与XHR Injection对responseText的执行方式不同,直接把responseText放在eval()函数里面执行。
//获取XMLHttpRequest对象,考虑兼容性。
var getXmlHttp = function(){
var obj;
if (window.XMLHttpRequest)
obj = new XMLHttpRequest();
else
obj = new ActiveXObject("Microsoft.XMLHTTP");
return obj;
};
//采用Http请求get方式;open()方法的第三个参数表示采用异步(true)还是同步(false)处理
var xmlHttp = getXmlHttp();
xmlHttp.open("GET", "http://cdn.bootcss.com/jquery/3.0.0-beta1/jquery.min.js", true);
xmlHttp.send();
xmlHttp.onreadystatechange = function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
eval(xmlHttp.responseText);
//alert($);//可以弹出$,表明JS已经加载进来。click事件放在其它出会出问题,应该是还没加载进来
$("#btn1").click(function(){
alert($(this).text());
});
}
}

Script In Irame:在父窗口插入一个iframe元素,然后再iframe中执行加载JS的操作。
var insertJS = function(){alert(2)};
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var doc = iframe.contentWindow.document;//获取iframe中的window要用contentWindow属性。
doc.open();
doc.write("var insertJS = function(){};");
doc.close();

GMail Mobile:业内JS内容被注释,所以不会执行,在需要的时候,获取script中的text内容去掉注释,调用eval()执行。

/*
var ...
*/


HTML5新属性:async和defer属性
defer属性:IE4.0就出现。defer属声明脚本中将不会有document.write和dom修改。浏览器会并行下载其他有defer属性的script。而不会阻塞页面后续处理。注:所有的defer脚本必须保证按顺序执行的。


async属性:HTML5新属性。脚本将在下载后尽快执行,作用同defer,但是不能保证脚本按顺序执行。他们将在onload事件之前完成。


Firefox 3.6、Opera 10.5、IE 9和最新的Chrome和Safari都支持async属性。可以同时使用async和defer,这样IE 4之后的所有IE都支持异步加载。
没有async属性,script将立即获取(下载)并执行,期间阻塞了浏览器的后续处理。如果有async属性,那么script将被异步下载并执行,同时浏览器继续后续的处理。
总结: 对于支持HTML5的浏览器,实现JS的异步加载只需要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持HTML5的浏览器(IE可以用defer实现),可以采用以上几种方法实现。原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,你可以把它放在匿名函数中执行,也可以在onload中执行,也可以通过XHR注入实现,也可以创建一个iframe元素,然后在iframe中执行插入JS代码。

异步加载又叫非阻塞加载,浏览器在下载执行js的同时,还会继续进行后续页面的处理。主要有三种方式。
方法一:也叫Script DOM Element

但是这种加载方式执行完之前会阻止onload事件的触发,而现在很多页面的代码都在onload时还执行额外的渲染工作,所以还是会阻塞部分页面的初始化处理。
方法二:onload时的异步加载

这种方法只是把插入script的方法放在一个函数里面,然后放在window的onload方法里面执行,这样就解决了阻塞onload事件触发的问题。
注:DOMContentLoaded与load的区别。前者是在document已经解析完成,页面中的dom元素可用,但是页面中的图片,视频,音频等资源未加载完,作用同jQuery中的ready事件;后者的区别在于页面所有资源全部加载完毕。
方法三:其他方法
由于JavaScript的动态性,还有很多异步加载方法: XHR Injection、 XHR Eval、 Script In Iframe、 Script defer属性、 document.write(script tag)。
XHR Injection(XHR 注入):通过XMLHttpRequest来获取JavaScript,然后创建一个script元素插入到DOM结构中。ajax请求成功后设置script.text为请求成功后返回的responseText。

Script In Irame:在父窗口插入一个iframe元素,然后再iframe中执行加载JS的操作。

GMail Mobile:业内JS内容被注释,所以不会执行,在需要的时候,获取script中的text内容去掉注释,调用eval()执行。

HTML5新属性:async和defer属性
defer属性:IE4.0就出现。defer属声明脚本中将不会有document.write和dom修改。浏览器会并行下载其他有defer属性的script。而不会阻塞页面后续处理。注:所有的defer脚本必须保证按顺序执行的。

async属性:Html5新属性。脚本将在下载后尽快执行,作用同defer,但是不能保证脚本按顺序执行。他们将在onload事件之前完成。

Firefox 3.6、Opera 10.5、IE 9和最新的Chrome和Safari都支持async属性。可以同时使用async和defer,这样IE 4之后的所有IE都支持异步加载。
没有async属性,script将立即获取(下载)并执行,期间阻塞了浏览器的后续处理。如果有async属性,那么script将被异步下载并执行,同时浏览器继续后续的处理。
总结: 对于支持HTML5的浏览器,实现JS的异步加载只需要在script元素中加上async属性,为了兼容老版本的IE还需加上defer属性;对于不支持Html5的浏览器(IE可以用defer实现),可以采用以上几种方法实现。原理基本上都是向DOM中写入script或者通过eval函数执行JS代码,你可以把它放在匿名函数中执行,也可以在onload中执行,也可以通过XHR注入实现,也可以创建一个iframe元素,然后在iframe中执行插入JS代码。
三:延迟加载
有些JS代码在某些情况在需要使用,并不是页面初始化的时候就要用到。延迟加载就是为了解决这个问题。将JS切分成许多模块,页面初始化时只加载需要立即执行的JS,然后其它JS的加载延迟到第一次需要用到的时候再加载。类似图片的延迟加载。JS的加载分为两个部分:下载和执行。异步加载只是解决了下载的问题,但是代码在下载完成后就会立即执行,在执行过程中浏览器处于阻塞状态,响应不了任何需求。
解决思路:为了解决JS延迟加载的问题,可以利用异步加载缓存起来,但不立即执行,需要的时候在执行。如何进行缓存呢?将JS内容作为Image或者Object对象加载缓存起来,所以不会立即执行,然后在第一次需要的时候在执行。
1:模拟较长的下载时间:
利用thread让其sleep一段时间在执行下载操作。
2:模拟较长的JS代码执行时间

这段代码将使JS执行5秒才完成!
JS延迟加载机制(LazyLoad):简单来说,就是在浏览器滚动到某个位置在触发相关的函数,实现页面元素的加载或者某些动作的执行。如何实现浏览器滚动位置的检测呢?可以通过一个定时器来实现,通过比较某一时刻页面目标节点位置和浏览器滚动条高度来判断是否需要执行函数。

方案1:针对支持html5 webworker的现代浏览器方案:。


代码1.你的大量计算,放到一个js文件中。如下:

//job.js
onmessage =function (evt){
  //do massive job.在这里你进行大量耗时的计算过程。
  postMessage( data );//将计算结果的数据发送会主线程
}


你的页面代码:

<!DOCTYPE HTML>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 <script type="text/javascript">
//WEB页主线程
var worker =new Worker("job.js"); //创建一个Worker对象并向它传递将在新线程中执行的脚本的URL
 worker.postMessage('开始计算');
 worker.onmessage =function(evt){//接收worker传过来的数据函数
   console.log(evt.data);//输出worker发送来的数据,这里就获取到了大量计算的结果。
 }
 </script>
 </head>
 <body></body>
</html>


方案2:对于不支持WebWorker线程的浏览器。

可以考虑分批处理。即是说创造一个间隔定时器setInterval。

每隔一小段时间,处理大量数据中的一部分。


这样就可以避免大量计算导致浏览器卡死。

大致代码如下(这里是简单的例子。具体情况具体分析。):

假设我们要计算1000万个数据的和。

var jobData =  [];//假设是一个数组。里面有1000万个数据。
function sliceJob(){
    var num = (jobData.length / 100) + 1;//把任务数据划分为100份。
    var portion = 100000;//每份有10万个数字。
    var addition = 0;//这里用来保存最后的结果。一开始是0;
    var intv = setInterval(function(){
        if(num--){
            //然后每一份结果。
            additoin += every;
        } else {
            计算最后一份,然后输出结果。
            alert('最终结果是:', addition);
            window.clearInterval(intv);
        }
    }, 50);
}

 

此外。jQuery的deferred对象无法实现你的要求。

因为deferred对象的目的是为了串行处理异步过程。

但是异步过程在执行的过程中,如果耗时过长,仍然会阻塞浏览器线程,导致浏览器不可操作(卡死)。

唯一的一个例外是$.ajax。$.ajax方法也会返回一个Deferred对象。但是由于该异步过程是用的XMLHttpRequest。而xhr默认是异步执行的,相当于另起一个线程,因此不会阻塞浏览器县城。



按我个人的理解
使用deffered对象是一种回调函数的解决方案,在执行js代码的时候,页面实际上还是被卡住的

我觉得最靠谱的解决方案还是使用ajax请求另外一个地址,把这些操作交给后台脚本执行并返回结果,只有这样,页面才不会被阻塞(就好像动态加载分页一样,会在html上显示一个loading,但你依然可以进行其他操作,页面也不会卡住)

你如果一定要用js的话,可以参考一下nodeJS,这样js就可以在服务器端运行。。。

使用 ajax技术 异步处理 enecy=true


崇明县19321009502: 执行一句js语句可以耗时一毫秒吗 -
蛮征祛风: setTimeout(function () { }, 1000); //1000 毫秒 该方法执行一次 setInterval(function () { }, 1000); //该方法重复执行

崇明县19321009502: 网页JAVASCRIPT脚本需长时间才能运行完成 -
蛮征祛风: 脚本长时间运行的问题通常是由两个问题引起的:过长的、过深嵌套的函数调用或者是进行大量的处理循环.第一种情况差不多需要重构你的程序,将函数或者对象之间解耦,减少相互依赖;第二种情况可以通过展开循环体(通过“达夫设备”之类的算法来完成,适用于处理大数组的情况)、分块处理(通过多个定时器分开调用处理该循环,使用于处理的数据无须同步按顺序完成的情况)等

崇明县19321009502: javaScript 中怎么延迟执行一个方法? -
蛮征祛风: setTimeout("test()","2000"); //2000毫秒后执行test()函数,只执行一次.setInterval("test()","2000"); //每隔2000毫秒执行一次test()函数,执行无数次.扩展资料: 1、setTimeout() 方法用于在指定的毫秒数后调用函数或计算...

崇明县19321009502: 如何让脚本中的一条命令执行一分钟在执行下一条命令?JS怎么写? -
蛮征祛风: 展开全部<br><br>setTimeout延迟执行函数<br>setInterval 循环执行函数<br>你可以百度一下这两个函数的用法,给你提供这个思路,另外给你一个例子:<br>setInterval("myInterval()",1000);//1000为1秒钟<br>function myInterval()<br>{<br> alert('aaaaaaaaa');<br> }

崇明县19321009502: js每隔五分钟执行一个函数 -
蛮征祛风: window.onload=function(){<br> //假设这里每个五分钟执行一次test函数<br> test();<br>}<br>function test(){<br> alert("每个五分钟一次");<br> setTimeout(test,1000*60*5);//这里的1000表示1秒有1000毫秒,1分钟有60秒,5表示总共5分钟<br>}

崇明县19321009502: 请问javascript里面如何实现延时几秒的操作??
蛮征祛风: &lt;html&gt; &lt;head&gt; &lt;script language="javascript"&gt; j=10; function func(){ document.getElementById("div1").innerText=j; j++; setTimeout("func()",1000); } &lt;/script&gt; &lt;/head&gt; &lt;body onload="func();"&gt; &lt;div id="div1"...

崇明县19321009502: js for循环中如何延迟执行 -
蛮征祛风: 需要准备的材料分别有:电脑、html编辑器、浏览器.1、首先,打开html编辑器,新建html文件,例如:index.html.2、在index.html中的<script>标签,输入js代码:.for (var i = 0; i< 3; i++) { var a = i + ':' + (new Date());$('body').append(a); for (var j = 0; j < 700000000; j++); }3、浏览器运行index.html页面,此时循环中的内容被延迟执行了.

崇明县19321009502: js指定一个时间执行一个函数
蛮征祛风: 使用JS是可以让函数不直接执行的,而是在过了一个指定的时间间隔后才执行

崇明县19321009502: JavaScript里有Timeout方法,可以使另一个函数经过(如3秒)后执行,并且只执行一次, -
蛮征祛风: 您好,请用C#的多线程.sleep(3000)一下,就实现了.

崇明县19321009502: js 的延迟跳转要怎么写,比如延迟执行一个函数
蛮征祛风: function 函数名(){ 逻辑......}setTimeout("函数名",3000); //3秒后执行"函数名"

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网