JavaScript定时器分析

简介:

一、事件循环

JavaScript是单线程,同一个时间只能做一件事情,所以执行任务需要排队。如果前一个耗时很长,那么下一个只能等待。

1)两种任务

为了更好的处理任务,JavaScript语言的设计者将任务分为两种:同步任务(synchronous)与异步任务(asynchronous)。

同步任务:在主线程上排队执行的任务。

异步任务:放在“任务队列”(task queue)中,只有当主线程空了,才会将“任务队列”中的任务放到主线程中。

这就是JavaScript的运行机制,这个过程会不断重复,这个机制叫事件循环(Event Loop)。

2)事件循环

事件循环模型可以用下图描述,图片来自Philip Roberts的演讲《Help, I’m stuck in an event loop》:

1. “WebAPIs”内的就是异步任务,包括DOM事件、Ajax和setTimeout。

2. “callback queue”内的是一个任务队列,包括click、load、done。

3. “stack”内的就是同步任务,只有当“stack”内的清空后,才会去轮询任务队列。

下面是一段代码说明,图片中的内容是打印结果,没什么悬念。

console.log('Hi');
setTimeout(function() {
  console.log('there');
},5000);
console.log('SJS');

1. 将log('Hi')方法入栈,这是个普通方法,出栈被引擎执行,输出“Hi”。

2. 将setTimeout方法入栈,这是个WebAPIs内的方法,出栈被引擎交给了相应模块,继续处理后面代码。

3. 将log('SJS')加入执行栈,出栈输出“SJS”。

4. 在setTimeout方法执行5秒后,到达触发条件,将setTimeout加到任务队列中。

5. 此时的执行栈为空,所以引擎开始轮询检查任务队列,有个setTimeout,于是将setTimeout加入执行栈中。

6. 在setTimeout中有个log('there')方法,将此方法入栈,输出“there”。

3)循环过程去取Ajax

下图展示的是主线程通过事件循环过程去取Ajax的消息:

 

二、定时器

定时器就是setTimeout(fn, delay)setInterval(fn, delay),定时器设定的延时是没有保证的。

如果setTimeout在时间点“n”被调用,那么执行定时器代码的JavaScript任务会在“n+delay”后才加入到消息队列中。

下图是JQuery的作者John Resig画的一张示例图:

左边是运行时间(单位ms),中间是JavaScript代码段,右边是代码计划开始执行时间

1. 0ms时JavaScript开始执行,2ms启动setTimeout,6ms加入Mouse Click,10ms启动setInterval,12ms加入setTimeout,20ms加入setInterval,30ms、40ms、50ms加入setInterval。

2. 第1段JavaScript执行了大概18ms,在18ms时,setTimeout过期了。

3. 按照单线程FIFO规则,接下来执行Mouse Click,再依次运行setTimeout和setInterval。

4. 第1个setInterval(20ms加入)还在排队等候中,30ms又要加入setInterval,但浏览器只让一个setInterval排队,其它的都废弃掉。

5. 第1个setInterval在36ms时开始执行,此程序需要执行6ms,第2个setInterval在40ms时开始排队,42ms时开始执行。

6. 第2个执行的setInterval在48ms时完成执行,50ms时第3个setInterval开始执行,不需要排队,直接运行。

 

三、分割任务

在JavaScript执行的时候,页面渲染的所有更新操作都要暂停。在执行繁忙的时候,可能会导致浏览器很卡或似乎要挂掉。

定时器,可以有效暂停一段JavaScript代码的执行,还可以将代码的各个部分,分解成不会让浏览器挂掉的碎片

1)未优化代码

长时间运行的任务,用手机扫二维码看效果会更明显

var tbody = document.getElementsByTagName("tbody")[0];
for (var i = 0; i < 20000; i++) { //创建20000个tr
  var tr = document.createElement("tr");
  for (var t = 0; t < 6; t++) { //每个tr有6个td
    var td = document.createElement("td");
    td.appendChild(document.createTextNode(i + "," + t));
    tr.appendChild(td);
  }
  tbody.appendChild(tr); //将tr添加到tbody中
}

2)已优化代码

利用定时器分解任务,将强循环转化为非阻塞操作:

//配置部分
var rowCount = 20000; 
var divideInto = 4; 
var chunkSize = rowCount / divideInto; //将20000分成4个部分
var iteration = 0; 

var table = document.getElementsByTagName("tbody")[0];

setTimeout(function generateRows() {
  var base = chunkSize * iteration; //计算上次中断的地方
  //添加tr部分
  for (var i = 0; i < chunkSize; i++) {
    var tr = document.createElement("tr");
    for (var t = 0; t < 6; t++) {
      var td = document.createElement("td");
      td.appendChild(document.createTextNode((i + base) + "," + t + "," + iteration));
      tr.appendChild(td);
    }
    table.appendChild(tr);
  }
  iteration++; //调度下一阶段
  if (iteration < divideInto) 
    setTimeout(generateRows, 0); 
}, 0);

 

 

参考资料:

JavaScript的计时器的工作原理

JavaScript:彻底理解同步、异步和事件循环(Event Loop)

从setTimeout说事件循环模型

JavaScript 运行机制详解:再谈Event Loop

JavaScript忍者秘籍

一家之言:说说 JavaScript 计时器的工作原理

分类:  JavaScript


本文转自 咖啡机(K.F.J) 博客园博客,原文链接:http://www.cnblogs.com/strick/p/6644528.html    ,如需转载请自行联系原作者
相关文章
|
4月前
|
JavaScript 前端开发 安全
JavaScript基础-定时器:setTimeout, setInterval
【6月更文挑战第13天】JavaScript中的`setTimeout`和`setInterval`是异步编程的关键工具,用于按计划执行代码。`setTimeout`在延迟后执行一次,而`setInterval`则周期性执行。常见问题包括忘记清除定时器导致内存泄漏,递归使用`setTimeout`可能引发无限递归,以及字符串代码执行的安全隐患。解决方法包括使用`clearTimeout`和`clearInterval`,设置递归终止条件,以及优先使用函数表达式。理解定时器的非精确性并采用错误处理策略也是实践中的重要技巧。通过示例展示了如何延迟显示消息和周期性打印计数。
60 2
|
4月前
|
Web App开发 前端开发 JavaScript
技术心得记录:瀑布流的布局原理分析(纯CSS瀑布流与JS瀑布流)
技术心得记录:瀑布流的布局原理分析(纯CSS瀑布流与JS瀑布流)
47 0
|
2月前
|
JavaScript 前端开发
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
这篇文章讨论了在JavaScript和Vue中如何避免定时器重复开启的问题,并提供了一个模拟场景和代码示例,演示了在开启新定时器前清除旧定时器的方法。
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
|
2月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
|
2月前
|
JavaScript 前端开发
|
2月前
|
Web App开发 JavaScript 前端开发
JavaScript——定时器为什么是不精确的
JavaScript——定时器为什么是不精确的
45 0
|
2月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
25 0
|
2月前
|
JavaScript 前端开发
js之一次性定时器
js之一次性定时器
23 0
|
3月前
|
JavaScript 前端开发
vue 模拟随机变速的动态打字特效【支持多行文本】(含css实现闪烁光标,js动态改变setInterval定时器的时间间隔)
vue 模拟随机变速的动态打字特效【支持多行文本】(含css实现闪烁光标,js动态改变setInterval定时器的时间间隔)
60 1
|
4月前
|
JavaScript 前端开发 安全
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作
安全开发-JS应用&原生开发&JQuery库&Ajax技术&加密编码库&断点调试&逆向分析&元素属性操作