JavaScript忍者秘籍——驯服线程和定时器
1.定时器和线程
- 设置和清除定时器
JavaScript提供了两种方式,用于创建定时器以及两个相应的清除方法。这些方法都是window对象上的方法。
| 方法 | 格式 | 描述 |
| setTimeout | id=setTimeout(fn,delay) | 启动一个定时器,在一段时间(delay)之后执行传入的callback,并返回该定时器的唯一标识 |
| clearTimeout | clearTimout(id) | 如果定时器还未触发,传入定时器标识即可取消该定时器 |
| setInterval | id=setInterval(fn,delay) | 启动一个定时器,在每隔一段时间(delay)之后都执行传入的callback,并返回该定时器的唯一标识 |
| clearInterval | clearInterval(id) | 传入间隔定时器标识,即可取消该间隔定时器 |
- timeout 与 interval 之间的区别
setTimeout(function repeatMe(){
/* Some long block of code... */
setTimeout(repeatMe,10);
},10);
setInterval(function(){
/* Some long block of code... */
},10)
在setTimeout()代码中,要在前一个callback回调执行结束并延迟10秒以后,才能再次执行setTimeout()。而setInterval()则是每隔10毫秒就尝试执行callback回调,而不关心上一个callback是何时执行的。
2.定时器延迟的最小化及其可靠性
在IE浏览器中,当我们队setInterval()设置0毫秒的延迟时,该定时器的callback回调只会执行一次。和使用setTimeout()的效果一样。
3.处理昂贵的计算过程
JavaScript的单线程本质可能是JavaScript复杂应用程序开发中的最大"陷阱"。如果一个脚本的运行时间超过5秒,有些浏览器将弹出一个对话框警告用户该脚本"无法响应"。而其他浏览器,比如iPhone上的浏览器,将默认终止运行时间超过5秒钟的脚本。
例如,一个长时间运行的任务:
<table><tbody></tbody></table>
<script>
var tbody = document.getElementsByTagName("tbody")[0];
for(var i = 0;i < 20000; i++){
var tr = document.createElement("tr");
for(var t=0; t<6; t++){
var td = document.createElement("td");
td.appendChild(document.createTextNode(i + "," + t));
tr.appendChild(td);
}
tbody.appendChild(tr);
}
</script>
在本例中,我们创建了240000个DOM节点,并使用大量的单元格来填充一个表格。这是非常昂贵的操作,明显会增加浏览器的执行时间,从而阻止正常的用户交互操作。这种情况下我们可以引入定时器,在代码执行的时候定期暂时休息,示例如下:
// 建立数据
var rowCount = 20000;
var divdeInto = 4;
var chunkSize = rowCount/divideInto;
var iteration = 0; var table = document.getElementsByTagName("tbody")[0]; // 计算上次中断的地方
setTimeout(function generateRows(){
var base = (chunkSize) * iteration;
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);
在本例中,我们将冗长的操作拆分成四个小步骤,每个步骤创建自己的DOM节点。这些小步骤,不太可能让浏览器挂掉。
4.中央定时器控制
同时创建大量的定时器,将会在浏览器中增加垃圾回收任务发生的可能性。大致说来,垃圾回收就是浏览器遍历其分配过的内存,并试图删除没有任何应用的未使用对象的过程。有些浏览器可以很好地处理这种情况,有些浏览器的垃圾回收周期则很长。比如,一个动画在某个浏览器中很流畅,但在另一个浏览器中却很卡顿,减少同时使用定时器的数量,就能解决这种问题,这也是为什么现代动画引擎都使用一种称为中央定时器控制的技术。
在多个定时器中使用中央定时器,可以带来很大的威力和灵活性。
● 每个页面在同一时间只需要运行一个定时器。
● 可以根据需要暂停和恢复定时器。
● 删除回调函数的过程变得很简单。
示例如下:
// 声明一个定时器控制对象
var timers = {
// 记录状态
timerID: 0,
timers: [],
// 创建添加处理程序的函数
add: function(fn){
this.timers.push(fn);
},
// 创建开启定时器的函数
start: function(){
if (this.timerID) return;
(function runNext(){
if(timers.timers.length > 0){
for(var i = 0; i < timers.timers.length; i++){
if(timers.timers[i]() === false){
timers.timers.splice(i,1);
i--;
}
}
timers.timerID = setTimeout(runNext, 0);
}
})();
},
// 创建停止定时器的函数
stop: function(){
clearTimeout(this.timerID);
this.timerID = 0;
}
};
在上述代码中,首先创建了一个中央控制结构,我们可以在该结构上添加任意数量的定时器回调函数,而且还可以通过它,启动和停止该结构的执行。此外,在任何时候如果callback回调函数返回了false值,都允许将其删除。
一开始,所有的回调函数都存储于一个名为timers的数组中,还包括当前定时器的一个ID。这些变量是定时器唯一需要维护的内容。
add()方法接受一个callback回调,并简单将其添加到timers数组中。真正核心的方法是start()方法。在该方法内,首先确认没有定时器在运行,如果确认没有定时器在执行,立即执行一个即时函数来开启中央定时器。
在即时函数内,如果注册了处理程序,就遍历执行每个处理程序。如果有处理程序返回false,我们就从数组中将其删除,最后进行下一次调度。
5.异步测试
中央定时器控制带来很大便利的另外一种情形就是在执行异步测试的时候。下面是一个简单的异步测试套件:
(function(){
// 保存状态表
var queue = [], paused = false;
// 定义测试注册的函数
this.test = function(fn){
queue.push(fn);
runTest();
};
// 定义停止测试的函数
this.pause = function(){
paused = true;
};
// 定义恢复测试的函数
this.resume = function(){
paused = false;
setTimeout(runTest, 1);
};
// 运行测试
function runTest(){
if (!paused && queue.length) {
queue.shift()();
if (!paused) resume();
}
}
})();
以上代码最重要的一个方面是,传递给test()方法的每个函数,最多只包含一个异步测试。它们的异步性由pause()和resume()的使用所定义。这两个方法分别在异步事件之前或之后进行调用。
JavaScript忍者秘籍——驯服线程和定时器的更多相关文章
- 【JavaScript忍者秘籍】定时器
- JavaScript忍者秘籍——函数(下)
概要:本篇博客主要介绍函数的一些类型以及常见示例 1.匿名函数 使用匿名函数的常见示例: window.onload = function(){ assert(true,'power!'); }; / ...
- JavaScript忍者秘籍——运行时代码求值
1. 代码求值机制 JavaScript中,有很多不同的代码求值机制. ● eval()函数 ● 函数构造器 ● 定时器 ● <script>元素 - 用eval()方法进行求值 作为定义 ...
- JavaScript忍者秘籍——原型
概要:本篇博客主要介绍JavaScript的原型 1.对象实例化 - 初始化的优先级 初始化操作的优先级如下: ● 通过原型给对象实例添加的属性 ● 在构造器函数内给对象实例添加的属性 在构造器内的绑 ...
- JavaScript忍者秘籍——闭包
概要:本篇博客主要介绍了JavaScript的闭包 1.闭包的工作原理 简单地说,闭包就是一个函数在创建时允许该自身函数访问并操作该自身函数之外的变量时所创建的作用域. 例如: var outerVa ...
- JavaScript忍者秘籍——函数(上)
概要:本篇博客主要介绍了JavaScript的函数. 1.第一型对象 JavaScript的函数可以像对象一样引用,因此我们说函数是第一型对象.除了可以像其他对象类型一样使用外,函数还有一个特殊的功能 ...
- 深入理解JavaScript中的函数操作——《JavaScript忍者秘籍》总结
匿名函数 对于什么是匿名函数,这里就不做过多介绍了.我们需要知道的是,对于JavaScript而言,匿名函数是一个很重要且具有逻辑性的特性.通常,匿名函数的使用情况是:创建一个供以后使用的函数.简单的 ...
- javascript 忍者秘籍读书笔记
书名 "学徒"=>"忍者" 性能分析 console.time('sss') console.timeEnd('sss') 函数 函数是第一类对象 通过字 ...
- 【JavaScript忍者秘籍】
随机推荐
- XP系统安装ArcGIS10.0需要修改的一个配置
1,右击我的电脑,查看属性. 2,选择“高级”选项卡,“启动和故障恢复”单击“设置”. 3,在“默认操作系统”中单击“编辑”: 4,在弹出的boot.ini文档中把操作系统改成相应的操作系统, ...
- easyui获取当前点击对象tabs的title
现在如果要关闭一个tab,只能点击该tab上面的x号.现增加双击tab使其关闭. 可使用jquery的bind函数绑定dblclick双击事件 tabs的关闭方法为close 要传一个title参数表 ...
- WCF双工学习笔记
WCF双工的作用在于服务端执行某个方法的时候调用客户端的方法,有点类似委托的感觉,实际项目中在什么情况下使用还没想到. WCF双工支持两种bind,一是nettcp.另一个是wsDualHttp,这里 ...
- C#新功能--命名参数与可选参数
C#新功能--命名参数与可选参数 可能是篇幅太短了,又被打入冷宫了.先重发一篇加上可选参数.本来不想加这个呢,因为可选参数可能大家用的会多点.其实这 两个在VB中早就有了,在C#中,只有在.net4以 ...
- MFC控件(8):command button与syslink control
在VS 2008里MFC多了4种控件,分别是 split buttons ,command button , syslink controls和 network address controls. s ...
- 巧用百度Site App新组件为企业官网打造移动站
一年前我曾经详细介绍过百度Site App,时隔一年的发展,Site App再一次发生了翻天覆地的变化:自定义程度更高.新增电话地图组件.增加流量统计.增加广告管家.生成APP客户端等功能,百度Sit ...
- JDBC--手动开启Connection事务
三层架构中的业务逻辑层是处理业务逻辑的部分,很多时候需要调用多步Dao层的增删改操作,这就涉及到使用事务保证数据的一致性. Connection接口自带的事务机制需要保证多步SQL操作使用相同的连接对 ...
- 人生在于折腾:php实现下载导出xx.tar.gz
刚接到这样的需求,其实我是拒绝的.我甚至很有耐心地和pm商量,扔个csv不就好了么? pm:对方需要一个csv打包成.tar.gz的包,他们是linux server,这是硬性要求. 然后我开始折腾之 ...
- Python爬虫小白入门(六)爬取披头士乐队历年专辑封面-网易云音乐
一.前言 前文说过我的设计师小伙伴的设计需求,他想做一个披头士乐队历年专辑的瀑布图. 通过搜索,发现网易云音乐上有比较全的历年专辑信息加配图,图片质量还可以,虽然有大有小. 我的例子怎么都是爬取图片? ...
- 使用highcharts显示mongodb中的数据
1.mongodb数据表相关 # 显示数据库 show dbs # 数据库 use ceshi # 显示表 show tables # 创建集合 db.createCollection('infoB' ...