《无所不能的JavaScript编程系列:setTimeout 简笔》

前言:问题引出

  JavaScript中会常常用到setTimeout来推迟一个函数的执行,如:javascript

1 setTimeout(function(){alert("Hello World");},1000)

  它的意思是会在执行到这句话后延迟1秒钟(1000毫秒)来弹出alert窗口。java

  那么再看这一段:
web

1 function a() {
2     setTimeout(function() {alert(1)}, 0);
3     alert(2);
4 }

  注意,这段代码中的setTimeout延迟设为了0,就是延迟0毫秒,貌似是不作任何延迟马上执行。但实际的执行结果确是先弹出2再弹出1,这是为何呢?JavaScript API文档明肯定义第二个参数意义为隔多少毫秒后,回调方法就会被执行。这里设成0毫秒,理所固然就当即被执行了!?这得从Javascript调用堆栈(call stack)和setTimeout的功能提及。编程

问题剖析

  首先,JavaScript引擎是单线程运行的,浏览器不管在何时都有且只有一个线程在运行JavaScript程序,即同一时间只执行一条代码,因此每个JavaScript代码执行块会“阻塞”其它异步事件的执行。浏览器

  其次,和其余的编程语言同样,Javascript中的函数调用也是经过堆栈实现的。如上例中,在执行函数a的时候,函数a先入栈,若是不给alert(1)加setTimeout,那么alert(1)第2个入栈,最后是alert(2)。但如今给alert(1)加上setTimeout后,alert(1)就被加入到了一个新的堆栈中等待,并“尽量快”的执行。这个尽量快就是指在a的堆栈完成后就马上执行,所以实际的执行结果就是先alert(2),再alert(1)。在这里setTimeout其实是让alert(1)脱离了当前函数调用堆栈。异步

扩展:AJAX是否真的异步?

  既然说JavaScript是单线程运行的,那么XMLHttpRequest在链接后是否真的异步?async

  其实请求确实是异步的,不过这请求是由浏览器新开一个线程请求,当请求的状态变动时,若是先前已设置回调,这异步线程就产生状态变动事件放到 JavaScript引擎的处理队列中等待处理,当任务被处理时,JavaScript引擎始终是单线程运行回调函数,具体点即仍是单线程运行onreadystatechange所设置的函数。编程语言

扩展:setTimeout的 应用场景

1、解决双击事件触发单击事件的冲突函数

  提示:默认双击会先触发单击事件,使用延迟单击事件进行处理。spa

 1 function click(){
 2   isdb=false;
 3   window.setTimeout(cc, 500)
 4   function cc(){
 5     if(isdb!=false)return;
 6     alert("单击")
 7   }
 8 }
 9 function dblclick(){
10   isdb=true;
11   alert("双击")
12 }

2、解决双击事件触发单击事件的冲突

  AJAX请求后台,调用webservice或调用大量数据查询等状况形成前台一直处于loading加载框的状况,可使用setTimeout来解决。

  部分JQ源码以下:

1 if ( s.async && s.timeout > 0 ) {
2     timeoutTimer = setTimeout(function() {
3         jqXHR.abort("timeout");
4     }, s.timeout );
5 }

编后语

  本博文简单介绍了setTimeout和JS单线程的知识,这块水其实很深,但这边只作一个随笔。有兴趣的同窗,推荐阅读jQuery做者John的一篇文章:How JavaScript Timers Work,你会对JavaScript单线程本质和setTimeout以及setInterval有更加深入的理解。

  http://ejohn.org/blog/how-javascript-timers-work/ 

  当你理解了JS的单线程和堆栈原理,那在使用JS进行高级程序编写中,必然会驾轻就熟。

  当你还在说JavaScript是一门玩具型的脚本语言时,它也在远处嘲笑你对它不够了解。

相关文章
相关标签/搜索