理解javascript定时器中的单线程


Posted in Javascript onFebruary 23, 2016

一、JavaScript 引擎是单线程的

可以从下面的代码中看到,第一个用setTimeout中的代码是死循环,由于是单线程,下面的两个定时器就没机会执行了。

<script type="text/javascript">
 setTimeout( function(){ while(true){} } , 100); 
 setTimeout( function(){ alert('你好!setTimeout'); } , 200); 
 setInterval( function(){ alert('你好!setInterval'); } , 200); 
</script>

浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现3个常驻线程:javascript引擎线程,GUI渲染线程,浏览器事件触发线程。

JavaScript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来然后加以处理,浏览器无论再什么时候都只有一个JS线程在运行JS程序。
GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
浏览器事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。

理解javascript定时器中的单线程

由上图可看出,浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看作是浏览器派给它的各种任务,JavaScript引擎一直等待着任务队列中任务的到来,由于单线程关系,这些任务得进行排队,一个接着一个被引擎处理。

t1、t2....tn表示不同的时间点,tn下面对应的小方块代表该时间点的任务。

t1时刻:

1、GUI渲染线程
2、浏览器事件触发线程:

在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由图可知,对于JavaScript引擎线程来说,这事件是由其它线程异步传到任务队列尾的,由于引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理。
3、定时触发线程:
这里的浏览器模型定时计数器并不是由JavaScript引擎计数的,因为JavaScript引擎是单线程的,如果处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,所以队列中的定时事件是异步事件。
4、在这t1的时间段内,继鼠标点击事件触发后,先前已设置的setTimeout定时也到达了,此刻对JavaScript引擎来说,定时触发线程产生了一个异步定时事件并放到任务队列中,该事件被排到点击事件回调之后,等待处理。同理,还是在t1时间段内,接下来某个setInterval定时器也被添加了,由于是间隔定时,在t1段内连续被触发了两次,这两个事件被排到队尾等待处理。
5、ajax异步请求:
浏览器新开一个http线程请求,当请求的状态变更时,如果先前已设置回调,这异步线程就产生状态变更事件放到JavaScript引擎的处理队列中等待处理。
二、任务的执行顺序不同,显示结果也不同

1)未使用setTimeout函数

在网上找到的一段代码实例,这里用来演示一下。

<a href="#" id="doBtn">do something</a>
<div id="status"></div>
<script type="text/javascript">
  var doBtn = document.getElementById('doBtn')
   , status = document.getElementById('status');

  function sleep(ms) {
  var start = new Date();
  while (new Date() - start <= ms) {}
  }
  
  doBtn.onclick = function(e) {
   status.innerHTML = 'doing...please wait...'; 
   sleep(3000); // 模拟一个耗时较长的计算过程,3s
   status.innerHTML = 'done'; 
   return false;
  };
</script>

我在firefox中执行了上面的代码。计划是点击“do something”按钮,然后显示“doing...please wait...”,接着执行sleep,最后显示“done”。

但是结果是点击后,浏览器卡住3秒左右,最后直接显示done。

分析下看出,在做status.innerHTML设置的时候,是需要执行GUI渲染线程的,但是现在还在执行JavaScript引擎线程,而JavaScript引擎线程与GUI渲染线程是互斥的,所以就最后显示了done。

 2)使用了setTimeout函数

<a href="#" id="doBtn2">do something timer</a>
<div id="status2"></div>
<script type="text/javascript">
  var doBtn2 = document.getElementById('doBtn2')
   , status2 = document.getElementById('status2');

  function sleep2(ms) {
  var start = new Date();
  while (new Date() - start <= ms) {}
  }
  
  doBtn2.onclick = function(e) {
   status2.innerHTML = 'doing...please wait...'; 
   setTimeout(function() {
   sleep2(3000); 
   status2.innerHTML = 'done'; 
   }, 100); 
   return false;
  };
</script>

在“doing...please wait...”后面加了个setTimeout,延时执行,给了浏览器渲染的时间,这个时候会显示出“doing...please wait...”的字样,然后执行sleep函数,最后显示“done”。

后面有网友发现在firefox中不起作用,的确有这个问题,后面我修改了一下代码,将局部变量的声明,onclick的绑定放到了window.onload事件中,等页面结构加载完成后,我再做脚本操作。

<script type="text/javascript">
  function sleep(ms) {
  //...
  }
  window.onload = function() {
   var doBtn = document.getElementById('doBtn'),
   status = document.getElementById('status');
   
   var doBtn2 = document.getElementById('doBtn2')
    , status2 = document.getElementById('status2');
    
   doBtn.onclick = function(e) {
    //...
   };
   doBtn2.onclick = function(e) {
    //...
   };
  };
</script>

以上就是本文的全部内容,希望对大家的学习有所帮助。

Javascript 相关文章推荐
基于jQuery的message插件实现右下角弹出消息框
Jan 11 Javascript
firefox下input type=&quot;file&quot;的size是多大
Oct 24 Javascript
js与jQuery 获取父窗、子窗的iframe
Dec 20 Javascript
推荐4个原生javascript常用的函数
Jan 12 Javascript
JavaScript使用indexOf获得子字符串在字符串中位置的方法
Apr 06 Javascript
第五篇Bootstrap 排版
Jun 21 Javascript
javascript表单控件实例讲解
Sep 13 Javascript
基于KO+BootStrap+MVC实现的分页控件代码分享
Nov 07 Javascript
老生常谈ES6中的类
Jul 31 Javascript
解决IE11 vue +webpack 项目中数据更新后页面没有刷新的问题
Sep 25 Javascript
3分钟读懂移动端rem使用方法(推荐)
May 06 Javascript
vue中可编辑树状表格的实现代码
Oct 31 Javascript
如何使用PHP+jQuery+MySQL实现异步加载ECharts地图数据(附源码下载)
Feb 23 #Javascript
教你如何终止JQUERY的$.AJAX请求
Feb 23 #Javascript
JQUERY的AJAX请求缓存里的数据问题处理
Feb 23 #Javascript
javascript每日必学之多态
Feb 23 #Javascript
javascript每日必学之继承
Feb 23 #Javascript
JS实现的仿QQ空间图片弹出效果代码
Feb 23 #Javascript
理解javascript封装
Feb 23 #Javascript
You might like
php一些公用函数的集合
2008/03/27 PHP
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2611816 bytes)
2014/11/08 PHP
php中关于长度计算容易混淆的问题分析
2016/05/27 PHP
php版微信公众账号第三方管理工具开发简明教程
2016/09/23 PHP
详解在YII2框架中使用UEditor编辑器发布文章
2018/11/02 PHP
javascript显示隐藏层比较不错的方法分析
2008/09/30 Javascript
ExtJS 简介 让你知道extjs是什么
2008/12/29 Javascript
JS中==与===操作符的比较
2009/03/21 Javascript
node.js中的http.response.removeHeader方法使用说明
2014/12/14 Javascript
深入理解JavaScript系列(44):设计模式之桥接模式详解
2015/03/04 Javascript
jquery点击改变class并toggle的实现代码
2016/05/15 Javascript
jQuery EasyUI菜单与按钮详解
2016/07/13 Javascript
浅谈AngularJs指令之scope属性详解
2016/10/24 Javascript
微信小程序开发之视频播放器 Video 弹幕 弹幕颜色自定义实例
2016/12/08 Javascript
AngularJS Toaster使用详解
2017/02/24 Javascript
View.post() 不靠谱的地方你知道多少
2017/08/29 Javascript
vuex 项目结构目录及一些简单配置介绍
2018/04/08 Javascript
JQuery 实现文件下载的常用方法分析
2019/10/29 jQuery
Selenium执行Javascript脚本参数及返回值过程详解
2020/04/01 Javascript
[02:18]DOTA2英雄基础教程 育母蜘蛛
2014/01/20 DOTA
python使用arp欺骗伪造网关的方法
2015/04/24 Python
python执行使用shell命令方法分享
2017/11/08 Python
python微信跳一跳游戏辅助代码解析
2018/01/29 Python
Python使用MyQR制作专属动态彩色二维码功能
2019/06/04 Python
Django 框架模型操作入门教程
2019/11/05 Python
pytorch快速搭建神经网络_Sequential操作
2020/06/17 Python
video实现有声音自动播放的实现方法
2020/05/20 HTML / CSS
改变生活的男士内衣:SAXX Underwear
2019/08/28 全球购物
在对linux系统分区进行格式化时需要对磁盘簇(或i节点密度)的大小进行选择,请说明选择的原则
2012/11/24 面试题
贷款委托书范本
2014/04/08 职场文书
2014年工程师工作总结
2014/11/25 职场文书
呼兰河传读书笔记
2015/06/30 职场文书
python实现三阶魔方还原的示例代码
2021/04/28 Python
MySQL中distinct和count(*)的使用方法比较
2021/05/26 MySQL
Arthas排查Kubernetes中应用频繁挂掉重启异常
2022/02/28 MySQL
深入理解pytorch库的dockerfile
2022/06/10 Python