继前篇 谈谈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>
此时浏览器显示结果为
或者为
*******站在巨人的肩膀上