JavaScript的setTimeout与setInterval执行时机
继前篇 谈谈JavaScrip的异步实现 ,我们知道JavaScript引擎是单线程的,所有的js的代码都将在这个单线程中执行。像浏览器事件、计时器等异步只是个幌子,异步时js并没有多个线程在执行,而是都排列在一个待执行队伍中。
setTimeout的使用方法
setTimeout(function(){},time)--可以正确执行。
setTimeout("js语句",time)--可以正确执行。 js语句可以是多条语句。
setTimeout(fun,time)
只引用函数名字,也可运行,但是要注意的是:如果fun是某个对象的方法,则fun函数内的this此时被当做window。
eg:
var obj = { "p1": "obj的属性p1", "fun": function () { alert(this.p1); } }; setTimeout(obj.fun, 1000);
运行后的结果,是undefined。用函数式对象定义对象也是此种结果。用方法1则可以输出正确的结果。
setTimeout(fun(),time)--不正确使用
不能正确执行,因为fun()会立即执行,没有延迟time时间后执行。
执行时机
当页面初始化时,setTimeout与setInterval的回调与队列中其它回调执行次序是怎样的?《JavaScript权威指南》中介绍的,setTimeout的回调发生在所有的事件都处理完,这句到底是不是对的?带着这些疑问,我们先来看个例子:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 <meta http-equiv="content-type" content="text/html;charset=utf-8"> 6 <script type="text/javascript" src="a.js"></script> 7 <script type="text/javascript"> 8 var before_x = 2; 9 </script> 10 <script type="text/javascript"> 11 var middle_x =3; 12 // 页面head中js执行完毕后,执行队列(页面尾部的js,定时器回调、事件回调,究竟哪个先执行,这个随机的); 13 setTimeout(function(){ 14 console.log("Timeout:"+(ax+before_x+middle_x+middle_y+bx+cx+domLoad_x+inner_x)); 15 },0); 16 var inter = setInterval(function(){ 17 console.log("Interval:"+(ax+before_x+middle_x+middle_y+bx+inner_x+domLoad_x+cx)); 18 clearInterval(inter); 19 },1) 20 var middle_y =1; 21 </script> 22 <script type="text/javascript" src="b.js"></script> 23 </head> 24 <body> 25 <input type="text" id="inp_click" onclick="inner_x =1;for(var i=0;i<1000000;i++){inner_x++};console.log('clickEvent:'+inner_x);"> 26 </body> 27 </html> 28 <script type="text/javascript" src="c.js"></script> 29 <script type="text/javascript"> 30 var start_time = new Date().getTime(); 31 for(var i= 1;i<1000000;i++){ 32 33 } 34 var end_time = new Date().getTime(); 35 console.log('after the html,wait:'+(end_time-start_time)); 36 var inp = document.getElementById("inp_click"); 37 var event = document.createEvent("MouseEvent"); 38 event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null); 39 inp.dispatchEvent(event); 40 var domLoad_x = 4; 41 </script>
a.js
1 var ax =1;
b.js
1 var bx = 2;
c.js
1 var cx =3;
chrome中绝大数执行正常;
偶尔会报错,特别是在页面刚在浏览器中打开时,出现以下错误
为什么正常?这是因为计时器在所有的js都执行完后才执行,包括页面尾部的js,模拟触发的事件回调。
为什么报错?这是因为计时器在页面尾部js执行前,先执行。
结论:页面head中js执行完毕后,执行队列(页面尾部的js,定时器回调、事件回调),究竟哪个先执行,这个随机的。
《JavaScript高级程序设计》介绍到DomContentLoaded事件时,提到了
setTimeout(function(){
//此处添加事件处理程序代码
},0);
用以弥补老式浏览器不支持DomContentLoaded。
如果你们还有兴趣,我们再来看个例子,至于为什么是这样,大家自己去思考。
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <title></title> 5 <meta http-equiv="content-type" content="text/html;charset=utf-8"> 6 <script type="text/javascript" src="a.js"></script> 7 <script type="text/javascript"> 8 var before_x = 2; 9 (function(window, undefined) { 10 var readyList = [], 11 isReady = 0, 12 readyBound = false, 13 init, 14 bindReady, 15 readyWait = 1; 16 init = function(wait) { // A third-party is pushing the ready event forwards 17 if (wait === true) { 18 readyWait--; 19 } // Make sure that the DOM is not already loaded 20 if (!readyWait || (wait !== true && !isReady)) { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). 21 // 确保body元素存在,这个操作是防止IE的bug 22 if (!document.body) { 23 return setTimeout(init, 1); 24 } // dom渲染完成标志设置为true 25 isReady = true; // If a normal DOM Ready event fired, decrement, and wait if need be 26 if (wait !== true && --readyWait > 0) { 27 return; 28 } // 绑定的渲染完成后的执行函数 29 if (readyList) { // 全部执行 30 var fn, i = 0, 31 ready = readyList; // 重置 32 readyList = null; 33 while ((fn = ready[i++])) { 34 fn.call(document); 35 } 36 } 37 } 38 }; // 初始化readyList事件处理函数队列 39 // 兼容不同浏览对绑定事件的区别 40 bindReady = function() { 41 if (readyBound) { 42 return; 43 } 44 readyBound = true; // $(document).ready()的嵌套调用时 45 // readyState: "uninitalized"、"loading"、"interactive"、"complete" 、"loaded" 46 if (document.readyState === "complete") { // 让它异步执行,使这个ready能延迟 47 return setTimeout(init, 1); 48 } // Mozilla, Opera and webkit 49 // 兼容事件,通过检测浏览器的功能特性,而非嗅探浏览器 50 if (document.addEventListener) { // 使用事件回调函数 51 document.addEventListener("DOMContentLoaded", 52 function() { 53 document.removeEventListener("DOMContentLoaded", arguments.callee, false); 54 init(); 55 }, 56 false); // 绑定回调到load,使之能一定执行 57 window.addEventListener("load", init, false); // IE 58 } else if (document.attachEvent) { // 确保在load之前触发onreadystatechange, 59 // 针对iframe情况,可能有延迟 60 document.attachEvent("onreadystatechange", 61 function() { // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). 62 if (document.readyState === "complete") { 63 document.detachEvent("onreadystatechange", arguments.callee); 64 init(); 65 } 66 }); // 绑定回调到一定执行能load事件 67 window.attachEvent("onload", init); // 如果是IE且非iframe情况下 68 // 持续的检查,看看文档是否已准备 69 var toplevel = false; 70 try { 71 toplevel = window.frameElement == null; 72 } catch(e) {} 73 (function() { 74 if (document.documentElement.doScroll && toplevel) { 75 if (isReady) { 76 return; 77 } 78 try { // If IE is used, use the trick by Diego Perini 79 // http://javascript.nwbox.com/IEContentLoaded/ 80 document.documentElement.doScroll("left"); 81 } catch(e) { 82 setTimeout(arguments.callee, 1); 83 return; 84 } // 执行在等待的函数 85 init(); 86 } 87 })(); 88 } 89 }; 90 window.ready = function(fn) { // 绑定上监听事件 91 bindReady(); // 如果dom已经渲染 92 if (isReady) { // 立即执行 93 fn.call(document); // 否则,保存到缓冲队列,等上面的监听事件触发时,再全部执行 94 } else if (readyList) { // 将回调增加到队列中 95 readyList.push(fn); 96 } 97 }; 98 })(window); 99 </script> 100 <script type="text/javascript"> 101 var middle_x =3; 102 // 页面head中js执行完毕后,执行队列(页面尾部的js,定时器回调、事件回调,究竟哪个先执行,这个随机的); 103 setTimeout(function(){ 104 console.log("Timeout:"+(ax+before_x+middle_x+middle_y+bx+cx+domLoad_x+inner_x)); 105 },0); 106 var inter = setInterval(function(){ 107 console.log("Interval:"+(ax+before_x+middle_x+middle_y+bx+inner_x+domLoad_x+cx)); 108 clearInterval(inter); 109 },5000) 110 var middle_y =1; 111 window.onload = function(event){ 112 console.log("onloaded"); 113 } 114 ready(function(event){ 115 console.log("dom loaded"); 116 }); 117 </script> 118 <script type="text/javascript" src="b.js"></script> 119 </head> 120 <body> 121 <input type="text" id="inp_click" onclick="inner_x =1;for(var i=0;i<1000000;i++){inner_x++};console.log('clickEvent:'+inner_x);"> 122 </body> 123 </html> 124 <script type="text/javascript" src="c.js"></script> 125 <script type="text/javascript"> 126 var start_time = new Date().getTime(); 127 for(var i= 1;i<1000000;i++){ 128 129 } 130 var end_time = new Date().getTime(); 131 console.log('after the html,wait:'+(end_time-start_time)); 132 var inp = document.getElementById("inp_click"); 133 var event = document.createEvent("MouseEvent"); 134 event.initMouseEvent("click",true,true,document.defaultView,0,0,0,0,0,false,false,false,false,0,null); 135 inp.dispatchEvent(event); 136 var domLoad_x = 4; 137 </script>
此时浏览器显示结果为
或者为
本文首发:http://www.cnblogs.com/sprying/archive/2013/05/29/3105268.html
参考:http://www.cnblogs.com/diguonianzhu/archive/2012/06/29/2570371.html
有任何问题,欢迎留言交流。
注意:已解决的问题,会在整理后删除掉。
*******站在巨人的肩膀上