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
有任何问题,欢迎留言交流。 注意:已解决的问题,会在整理后删除掉。

*******站在巨人的肩膀上

原文地址:https://www.cnblogs.com/sprying/p/3105268.html