其实,大家都知道Javascript的语言执行环境是单线程的,浏览器无论在什么时候都有且只有一个线程在运行Javascript程序。那Ajax发送异步请求怎么解释,setTimeout/setInterval定时执行回调函数又是怎么解释呢?

要说解释清楚这些问题,还得从浏览器内核处理定时器(setTimeout、setInterval)和响应浏览器事件说起。

浏览器内核允许多个线程异步执行,这些线程在内核制控下相互配合以保持同步。浏览器内核的实现至少有三个常驻线程:javascript引擎线程、GUI渲染线程、浏览器事件触发线程。除些以外,也有一些执行完就终止的线程:如Http请求线程等,这些异步线程都会产生不同的异步事件。

我们可以通过下面这张图来理解JavaScript引擎与另外那些线程之间的通信机制。

      从图中可以看出,Javascript引擎线程是基于时间驱动的(event-driven),有一个执行队列,由回调函数(event-handler)组成,每当产生一个回调函数都会放入到Javascipt任务队列中去等待被执行。只有前面的回调函数执行完毕,才会执行后面的回调函数,如果前面的回调函数执行时被阻塞,将会导致之后的回调函数无法被执行。这些回调函数可以源自 JavaScript引擎当前执行的代码块,如调用setTimeout/setInterval,也可来是界面元素鼠标点击事件,定时触发 器时间到达通知,异步请求状态变更通知等。
 
      我们可以写一段代码验证上面说法。
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" >
<title>测试</title>
<style>
*{margin:0;padding:0}
body{background-color:#000000; color: #ffffff}
</style>
</head> <body>
<script type="text/javascript">
setTimeout(function(){
while(true){
console.log("Do first CallBack");
}
},1000);
setInterval(function(){
console.log("Do second CallBack_____"+new Date().getTime());
},2000);
</script>
</body>
</html>

执行结果告诉我们,setInterval的回调函数一直没有被执行,在执行setTimeout回调函数是发生阻塞,导致执行队列后面的回调函数无法执行。

假如代码的调用方式是 setTimeout(func, 100),那么该代码被执行 100 毫秒之后,定时器的事件被触发。如果这个时候 JavaScript 引擎没有正在执行的其它代码,则与此定时器对应的 JavaScript 方法 func就可以被执行。否则的话,该 JavaScript 方法的执行就被加入到等待的队列中。当 JavaScript 引擎空闲的时候,会从这个队列中选择一个等待的 JavaScript 方法来执行。也就是说,虽然在调用 setTimeout()的时候设置的间隔时间是 100 毫秒,与之对应的 JavaScript 方法实际被执行的间隔有可能大于设定的 100 毫秒,取决于是否有其它代码正在被执行和执行所花费的时间。因此 setTimeout()的实际生效的间隔时间可能大于设定的时间。

而 setInterval()的执行方式与 setTimeout()有很大不同。假如代码的调用方式是 setInterval(func, 100),则每隔 100 毫秒,定时器的事件就会被触发。与 setTimeout()相同的是,如果当前 JavaScript 引擎空闲的话,则定时器对应的方法 func会被立即执行。否则的话,该 JavaScript 方法的执行就会被加入到等待队列中。由于定时器的事件是每隔 100 毫秒就触发一次,有可能某一次事件触发的时候,上一次事件的处理方法还没有机会得到执行,仍然在等待队列中。这个时候,这个新的定时器事件就被丢弃。需要注意的是,由于 JavaScript 引擎的这种执行方式,有可能定时器事件处理方法的两次被执行的实际时间间隔小于设定的间隔。比如上一个定时器事件的处理方法触发之后,等待了 50 毫秒才获得被执行的机会。而第二个定时器事件的处理方法被触发之后,马上就被执行了。那么这两者之间的时间间隔实际上只有 50 毫秒。因此,setInterval()并不适合实现精确的按固定间隔的调度操作。

总的来说,使用 setTimeout()和 setInterval()的时候,都不能满足精确的时间间隔。通过 setTimeout()设置的 JavaScript 方法的实际执行间隔时间不小于设定的时间,而通过 setInterval()设置的重复执行的 JavaScript 方法的间隔可能会小于设定的时间。

       那Ajax的异步请求又怎么解释呢?ajax请求是由浏览器新开一个线程来进行请求,它是把当请求的状态变更时,如果先前已设置回调,那么这回调函数将被放到 JavaScript引擎的处理队列中等待处理,这就看起来好像是javascript在进行异步多线程处理,其实不是的。

推荐一篇博客:How Javascript Timers Work.

谈谈Javascript线程的更多相关文章

  1. setTimeout setInterval 区别 javascript线程解释

    原文:http://www.iamued.com/qianduan/1645.html 今天看到这篇文章,学到了不少东西 特此发出来 和大家分享 JavaScript的setTimeout与setIn ...

  2. javascript线程解释(setTimeout,setInterval你不知道的事)---转载

    在工作中,可能我们经常遇到在有很多 setInterval 的页面, 再手动触发 setTimeout 的时候经常失败, 尤其是 jquery做动画的时候,一些渐入溅出的东西,很多东西都不被触发……, ...

  3. 再看JavaScript线程

    继上篇讨论了一些关于JavaScript线程的知识,我们不妨回过头再看看,是不是JavaScript就不能多线程呢?看下面一段很简单的代码(演示用,没考虑兼容问题): 代码判断一: <div i ...

  4. javascript线程解释(setTimeout,setInterval你不知道的事)

    john resig写的一篇文章: 原文地址:http://ejohn.org/blog/how-javascript-timers-work/ 作为入门者来说,了解JavaScript中timer的 ...

  5. 谈谈javaScript

    谈谈javaScript  (杰我学习) 一. 什么是JavaScript       人们通常所说的JavaScript,其正式名称为ECMAScript.这个标准由ECMA组织发展和维护.ECMA ...

  6. 《windows核心编程系列》十五谈谈windows线程栈

    谈谈windows线程栈. 当系统创建线程时会为线程预订一块地址空间区域,注意仅仅是预订.默认情况下预定的这块区域的大小是1MB,虽然预订这么多,但是系统并不会给全部区域调拨物理存储器.默认情况下,仅 ...

  7. JavaScript线程

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...

  8. 谈谈javascript中的prototype与继承

    谈谈javascript中的prototype与继承 今天想谈谈javascript中的prototype. 通常来说,javascript中的对象就是一个指向prototype的指针和一个自身的属性 ...

  9. Java:谈谈控制线程的几种办法

    目录 Java:谈谈控制线程的几种办法 join() sleep() 守护线程 主要方法 需要注意 优先级 弃用三兄弟 stop() resume suspend 中断三兄弟 interrupt() ...

随机推荐

  1. 滑动RecyclerView时出现异常: java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 6(offset:6).state:30

    RecyclerView 存在的一个明显的 bug 一直没有修复: java.lang.IndexOutOfBoundsException: Inconsistency detected. Inval ...

  2. C++文件读写总结

    在C++中如何实现文件的读写? 作者: infobillows 发表日期: 2007-04-03 21:33 点击数: 465 一.ASCII 输出 为了使用下面的方法, 你必须包含头文件<fs ...

  3. Linux Kernel ‘exitcode_proc_write()’函数本地缓冲区溢出漏洞

    漏洞名称: Linux Kernel ‘exitcode_proc_write()’函数本地缓冲区溢出漏洞 CNNVD编号: CNNVD-201311-061 发布时间: 2013-11-07 更新时 ...

  4. 【转】Android 源码编译make的错误处理--不错

    原文网址:http://blog.csdn.net/ithomer/article/details/6977386 Android源码下载:官方下载 或参考android源码下载方式 Android编 ...

  5. 【原】1.1RDD源码解读(一)

    1.RDD(Resilient Distributed DataSet)是Spark生态系统中最基本的抽象,代表不可变的.可并行操作的分区元素集合.RDD这个类有RDD系列所有基本的操作,比如map. ...

  6. mvc 4 Razor (@html.xx)语法大全以及应用

    Razor语法大全  @Html ASP.NET MVC 中@Html.Partial,@Html.Action,@Html.RenderPartial,@Html.RenderAction差别 对这 ...

  7. 【CSS】Intermediate2:Grouping and Nesting

    1.Grouping 2.Nesting If the CSS is structured well, there shouldn’t be a need to use many class or I ...

  8. nyoj VF函数

    大意就是: 在1到在10的9次方中,找到各个位数和为固定值s的数的个数, 首先我们确定最高位的个数,为1到9: 以后的各位为0,到9: 运用递归的思想,n位数有n-1位数生成 f(n)(s) +=f( ...

  9. linux内核系列(一)编译安装Linux内核 2.6.18

    1.配置环境 操作系统:CentOS 5.2 下载linux-2.6.18版本的内核,网址:http://www.kernel.org 说明:该编译文档适合2.6.18以上的Linux内核版本,只需所 ...

  10. Codeforces335B - Palindrome(区间DP)

    题目大意 给定一个长度不超过5*10^4的只包含小写字母的字符串,要求你求它的回文子序列,如果存在长度为100的回文子序列,那么只要输出长度为一百的回文子序列即可,否则输出它的最长回文子序列 题解 这 ...