从setTimeout分析浏览器线程

来源:互联网 发布:网络监控如何连接电脑 编辑:程序博客网 时间:2024/06/06 19:15

从setTimeout分析浏览器线程

[toc]
  今天接到阿里的面试电话,面试官很和善,聊聊天的形式不知不觉就是一个小时。本人接触前端不深,面试的时候问的几个问题也让我发现自身学习过程中思考太少,其中一个就是问到了setTimeout的工作机理,当时简单讲了讲我自己的想法,面试官也指出了其中的问题,现查阅资料重新整理记录。

1. setTimeout的表象

  setTimeout的定义很简单,教科书上说,setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式。回想平时的开发,setTimeout多用于定时器,轮播图,动画效果,自动滚动等。

1.1 案例一

var startTime = new Date();setTimeout(function () {    console.log(new Date() - startTime);}, 100);

  答案取决于同步执行的js需要占用的时间,MAX(同步执行的时间, 100)

1.2 案例二

setTimeout(function () {    func1();}, 0)func2();

  此处func2先执行,func1后执行

1.3 案例三

setTimeout(function () {    func1()}, 0)

setTimeout(function () {    func1()})

  0秒延迟,此回调将会放到一个能立即执行的时段进行触发。javascript代码大体上是自顶向下的,但中间穿插着有关DOM渲染,事件回应等异步代码,他们将组成一个队列,零秒延迟将会实现插队操作。不写第二个参数,浏览器自动配置时间,在IE,FireFox中,第一次配可能给个很大的数字,100ms上下,往后会缩小到最小时间间隔,Safari,chrome,opera则多为10ms上下。

2. 浏览器内核线程分析

  初学JavaScript时出现过一个误区:JavaScript引擎是多线程的,定时器回调函数是异步执行的。 事实上,JavaScript引擎是单线程的,其实单线程也有单线程的好处,可以简化很多问题。
  想说明白js的运行机制,不得不提到浏览器内核线程。通常来说,一个浏览器内核的实现至少有三个常驻线程:javascript引擎线程、GUI渲染线程、浏览器事件触发线程。此外还有些执行完即终止的线程,如Http请求线程,这些异步线程会产生不同的异步事件,下图阐明单线程JavaScript引擎与其他线程的通信。

浏览器内核

  由图可看出,浏览器中的JavaScript引擎基于事件驱动,由于是单线程,所有事件在队列中排队等待执行。
  上图 t1 - t2 … tn 表示不同时间点,t1时刻其他线程的工作状态为:
【GUI渲染线程】
  该线程负责渲染浏览器界面的HTML元素,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。该线程与JavaScript引擎线程互斥,因为JavaScript脚本是可操纵DOM元素的,在修改这些元素属性同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致。在JavaScript引擎运行脚本期间,浏览器渲染线程处于挂起状态。所以在脚本中执行对界面的更新操作,如添加、删除结点或改变结点的外观等更新并不会立即体现出来,这些操作将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来。
【事件触发线程】
  JavaScript脚本的执行不影响html元素事件的触发,在t1时间段内,用户点击鼠标被浏览器事件触发线程捕捉后形成一个鼠标点击事件,由其它线程异步传到任务队列尾。
【定时触发线程】
  浏览器模型定时计数器并不是由JavaScript引擎计数,因为单线程的JavaScript引擎处于阻塞状态无法计时,必须依赖外部来计时并触发,所以队列中的定时事件也是异步事件。

案例1 详见后面的案例分析
setTimeout与setInterval

setTimeout(function() {    /* 代码块... */    setTimeout(arguments.callee, 10);}, 10);setInterval(function(){    /* 代码块... */}, 10);

  两段代码看似效果相同,其实不然,第一段中回调函数内的setTimeout是JavaScript引擎执行后再设置新的setTimeout定时,理论时间间隔>=10ms;第二段自setInterval设置定时后,定时触发线程会不断的每隔10ms产生异步定时事件并放到任务队列尾,理论时间间隔<=10ms

案例2
ajax异步请求是否真异步

  XMLHttpRequest请求在连接后是异步的,请求是由浏览器新开一个线程,当请求状态变更时,若设置回调函数,异步线程产生状态变更事件放到JavaScript引擎的处理队列中以单线程等待处理

3. setTimeout()与setInterval()运行实例分析

3.1 setTimeout() 案例1

【代码1】

console.log(1)setTimeout(function () {console.log('a')}, 10);setTimeout(function () {console.log('b')}, 0);var sum = 0;for (var i = 0; i < 1000000; i ++) {    sum += i;}console.log(sum);setTimeout(function () {console.log('c');}, 0);

【结果1】
浏览器内核
【流程1】
浏览器内核

3.2 setTimeout() 案例2

【代码2】: for循环规模缩小

console.log(1)setTimeout(function () {console.log('a')}, 10);setTimeout(function () {console.log('b')}, 0);var sum = 0;for (var i = 0; i < 100000; i ++) {    sum += i;}console.log(sum);setTimeout(function () {console.log('c');}, 0);

【结果2】
浏览器内核
【流程2】
浏览器内核

3.3 setTimeout() 结果分析

  两段代码的区别在于for循环执行的时间不同,第一段代码的for循环执行时间大于10ms,所以console.log(‘a’)先被插入任务队列,等for循环执行结束后,console.log(‘c’)才被插入任务队列。第二段代码的for循环执行时间小于10ms,所以console.log(‘c’)先被插入任务队列。

3.4 setInterval()

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

console.log(1)var interval = setInterval(function () {    var date = new Date();    console.log(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds());}, 10);var sum = 0;for (var i = 0; i < 1000000; i ++) {    sum += i;}console.log(2);// 清除定时器setTimeout(function () {    clearInterval(interval);}, 100);

【结果】
浏览器内核
  可以看出,setInterval()前两次的间隔时间只有4ms。因为setInterval()第一次被触发后,里面的方法并没有马上被执行,而是等待同步代码执行结束后才被执行,这个过程用了6ms。所以当第一次方法执行过后4ms,第二次方法也被执行了。从setInterval()第二次被触发开始,后面几次的执行都没有被阻塞,所以间隔时间都在11ms左右。

3.5 setTimeout()与setInterval()案例总结

  总的来说,setTimeout()和setInterval()都不能满足精确的时间间隔。假如设定的时间间隔为10ms,则setTimeout(fn, 10)中的fn执行的时间间隔可能大于10ms,而setInterval(fn, 10)中fn执行的时间间隔可能小于10ms。

4. 利用setTimeout实现伪多线程优化交互

  javascript是单线程的,特点就是容易出现阻塞。如果一段程序处理时间很长,很容易导致整个页面hold住。
  setTimeout一个很关键的用法就是分片,如果一段程序过大,我们可以拆分成若干细小的块。例如上面的情况,我们将那一段复杂的逻辑拆分处理,分片塞入队列。这样即使在复杂程序没有处理完时,我们操作页面,也是能得到即使响应的。其实就是将交互插入到了复杂程序中执行。

Concurrent.Thread.create(function(){    for (var i = 0;i<1000000;i++) {        console.log(i);    };});$('#test').click(function  () {    alert(1);});

  对复杂代码段使用setTimeout分片执行实现了交互的优化

5. 真正的多线程:HTML5 Web Workers

  在HTML4中,js创建的程序都是单线程的,Web Workers 是在HTML5中新增的,用来在web应用程序中实现后台处理的一种技术。使用这个API可以非常容易的创建在后台运行的线程:

var worker = new Worker('*.js');// 后台线程是不能访问页面或窗口对象的// 但可通过发送消息和接受消息与后台线程传递数据worker.onmessage = function (e) {};worker.postMessage = function (e) {};
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 事业单位每年福利体检有乙肝怎么办 高考失利后我该怎么办 初三了科学总是考不好怎么办 初三学生三模考的不好怎么办 高考体检检查出乙肝怎么办 高考体检单丢了怎么办 小腿弯曲没发育好怎么办? 辅警考试体能测试不合格怎么办 跑步跑不动怎么办腿软 人胖跑步跑不动怎么办 不工作每天很累怎么办 电大本科文凭毕业证掉了怎么办 要求全日制本科自考本科怎么办 毕业证被学校扣了怎么办 自考本科档案没有密封怎么办 艺术生统考没过怎么办 本科科目没考过怎么办 军校体检条丢了怎么办 学美术考了大专怎么办 音基试唱音不准怎么办 音基证书丢了怎么办 职称计算机证书丢了怎么办 30岁了 开始怀旧怎么办 31岁了 缺乏运动怎么办 30多了还一事无成未来怎么办 导师说名额已满怎么办 在中国想当大官怎么办 站久了小腿变粗怎么办 苹果x锁屏延迟怎么办 被扇了巴掌耳痛怎么办 被扇了巴掌耳鸣怎么办 水兵舞不会扭胯怎么办 我的字写得很丑怎么办 二年级学生不会造句怎么办 w10下载种子文件失败怎么办 宝宝挂水手肿了怎么办 lol有英雄皮肤没英雄怎么办 qq聊天图标粉色钥匙怎么办 和舍友相处不来怎么办 被舍友偷了东西怎么办 体育生没过线怎么办