理解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 相关文章推荐
js multiple全选与取消全选实现代码
Dec 04 Javascript
简体中文转换繁体中文(实现代码)
Dec 25 Javascript
一个JavaScript防止表单重复提交的实例
Oct 21 Javascript
JS控制表格实现一条光线流动分割行的方法
Mar 09 Javascript
JavaScript将数字转换成大写中文的方法
Mar 23 Javascript
JavaScript编写带旋转+线条干扰的验证码脚本实例
May 30 Javascript
jQuery Easyui datagrid行内实现【添加】、【编辑】、【上移】、【下移】
Dec 19 Javascript
基于js 字符串indexof与search方法的区别(详解)
Dec 04 Javascript
详解小程序input框失焦事件在提交事件前的处理
May 05 Javascript
JavaScript页面加载事件实例讲解
Sep 01 Javascript
vue实现购物车选择功能
Jan 10 Javascript
JS事件循环机制event loop宏任务微任务原理解析
Aug 04 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中$_SERVER[PHP_SELF] 和 $_SERVER[SCRIPT_NAME]之间的区别
2009/09/05 PHP
php笔记之:有规律大文件的读取与写入的分析
2013/04/26 PHP
慎用preg_replace危险的/e修饰符(一句话后门常用)
2013/06/19 PHP
php解决抢购秒杀抽奖等大流量并发入库导致的库存负数的问题
2014/06/19 PHP
php实现的美国50个州选择列表实例
2015/04/20 PHP
了解PHP的返回引用和局部静态变量
2015/06/04 PHP
PDO::exec讲解
2019/01/28 PHP
PHP PDOStatement::fetchAll讲解
2019/01/31 PHP
浅谈laravel数据库查询返回的数据形式
2019/10/21 PHP
JSQL  一个 web DB 的封装
2010/05/05 Javascript
用jQuery中的ajax分页实现代码
2011/09/20 Javascript
JS Replace()的高级使用方法介绍
2013/06/29 Javascript
JS中mouseover和mouseout多次触发问题如何解决
2016/06/06 Javascript
jQuery实现ajax的叠加和停止(终止ajax请求)
2016/08/08 Javascript
js实现5秒倒计时重新发送短信功能
2017/02/05 Javascript
简单实现jQuery弹幕效果
2017/05/06 jQuery
Vue之Watcher源码解析(1)
2017/07/19 Javascript
使用jQuery实现简单的tab框实例
2017/08/22 jQuery
AngularJS实现的省市二级联动功能示例【可对选项实现增删】
2017/10/26 Javascript
canvas轨迹回放功能实现
2017/12/20 Javascript
vue-cli和v-charts实现可视化图表过程解析
2019/10/08 Javascript
JS实现的进制转换,浮点数相加,数字判断操作示例
2019/11/09 Javascript
Python中的条件判断语句与循环语句用法小结
2016/03/21 Python
Python的Twisted框架中使用Deferred对象来管理回调函数
2016/05/25 Python
python 去除txt文本中的空格、数字、特定字母等方法
2018/07/24 Python
Python绘制二维曲线的日常应用详解
2019/12/04 Python
详解python 降级到3.6终极解决方案
2020/02/06 Python
Python通过4种方式实现进程数据通信
2020/03/12 Python
亚洲领先的旅游体验市场:Voyagin
2019/11/23 全球购物
工程总经理工作职责
2013/12/09 职场文书
管理标语大全
2014/06/24 职场文书
化学教育专业自荐信
2014/07/04 职场文书
教师“一帮一”结对子活动总结
2015/05/07 职场文书
创业计划书之美甲店
2019/09/20 职场文书
React Hook用法示例详解(6个常见hook)
2021/04/28 Javascript
Django中celery的使用项目实例
2022/07/07 Python