在上一篇博客中,我对jQuery的队列(queue)机制和动画(animate)机制做了一个深入的解析,在animate的实现机制其核心是依靠queue来完成的,其中在jQuery的链式调用部分,之前我非常疑惑为什么animate内部只需要在第一次入队操作后进行一次出队,然后就可以在第一个动画完成后直接使用next()来实现下一个动画出队,这个问题一度让我非常的疑惑,通过接近三个小时的战斗(debugger对源码执行栈反复测试),然后仿然大悟,给自己一个鄙视的眼神,一度对自己的JavaScript底层机制信心满满的,怎么会困在这么愚蠢的问题上呢?

此刻我接收你们的鄙视,如果你不明白我为什么鄙视自己,那我建议你也先鄙视一下自己,因为这是web前端工程师最不应该忘记(或者不懂)的知识点,通常我们在初学JavaScript语言的时候,老师会特别的强调JavaScript是单线程脚本语言,这也是JavaScript相对Java、C#等语言最大的区别和特点。这么说确实是对的,但是别忽略了浏览器内核的存在,浏览器对JavaScript代码的解析执行也确实是单线程模式,但是浏览器内核不只有JavaScript引擎线程,它还有GUI线程、http网络请求线程、定时器触发线程、浏览器事件处理线程。接下来就一一介绍这些线程在浏览器引擎中所分担的工作机制,然后理解它们都是如何在浏览器引擎中相互配合的,再对这种机制下产生的问题逐个剖析。

一、浏览器常驻的线程

  • js引擎线程:解释执行js代码、用户输入、网络请求
  • GUI线程:绘制用户界面、与js主线程互斥
  • http网络请求线程:处理用户的get、post等请求,等返回结果后将回调函数堆入任务队列
  • 定时触发器线程:setTimeout、setInterval等待时间结束后把执行函数堆入任务队列中
  • 浏览器事件处理线程:将click、mouse等交互事件发生后将这些事件放入事件队列中

二、浏览器线程如何协调工作?

  • 图解解浏览器个线程协调工作流程(上图)
  • 通过队列机制理解JS引擎线程执行机制
  • 了解GUI渲染线程与JS引擎线程的关系
  • 理解UI多线程工作机制

1.浏览器UI主线程协调运转图在前面已经给出来了,通过这个图可以看到JS引擎的单线程内部是采用了队列的方式来实现的。关于队列的详细内部机制这里就不多做解析了,不是很了解的可以参考我的另一篇博客:jQuery使用(九):队列及实现原理、基于队列模拟实现animate()

2.GUI渲染线程和JS线程是互斥的关系,所谓互斥就是当JS引擎运行的时候,GUI渲染引擎就不能运行,反之则相反。这两个引擎的互斥关系比较难被察觉,在正常的程序执行过程中,程序的执行速度远远大于我们的视觉感官,但是有一个不正常的时候我们就很容易理解这种互斥关系了,比如当js代码出现了死循环的时候,你会发现点击按钮时按钮的默认变身效果不会出现,其实在点击按钮时的点击事件已经被异步的添加到了事件处理线程,当等到死循环结(相对较长事件的有限循环)束后,按钮就出现一次变色效果,然后事件回调函数被执行,返回结果。

3.我们前面讲到了JS引擎线程执行机制是基于队列,如果理解了队列就很容易理解其他异步线程(定时器处理线程、事件处理线程、网络请求线程),在通过处理后是这样把它们添加到JS线程中来的了。再解释它们的协调关系前,先来了解异步线程都做了什么,定时器线程当然就是然回调函数在指定的等待时间后,通过定时器处理线程添加到JS执行队列中;事件处理线程就很简单,事件处理引擎会时刻监听浏览器的各种事件,一旦被触发就将对应的回调函数添加到JS执行队列中;网络请求线程相对来说会比较复查,它要通过触发网络请求,通过三次网络握手四次网络挥手的过程,拿到网络请求的数据后,再将对应的请求数据交给JS线程。

4.UI多线程的协调工作就是通过异步线程做预处理,当异步线程处理完了以后,再将对应的函数,数据交给JS线程(入队),然后再由js线程做出队操作,一个一个执行。所以这里就会产生很多的相关链的问题。

三、执行栈、任务队列、事件循环

1.什么是执行栈?

  • 栈是先进后出(FILO)的数据结构
  • 执行栈中存放正在执行的任务,每一个任务叫做“帧”
function foo(c){
var a = 1;
bar(20);
console.log(a + c);
}
function bar(d){
var b = 2;
console.log(b + d);
}
foo(50);

先来理解执行栈的执行过程:

创建foo函数执行帧 --> 进入foo执行栈 ---> 创建bar函数执行帧 --> 进入bar执行栈 ---> 打印结果:22 --> bar弹出栈 --->打印结果:51 -->foo弹出栈。

执行栈相当于JS主线程,当执行栈为空时,主线程才会从执行队列中获取任务,这个过程是循环不断的,因为从执行队列中获取任务拿到代码执行就表示JS线程进入执行栈的过程,当前任务执行完JS执行栈又为空了,所以有循环进入执行队列中获取队列的第一个任务,在进入JS线程中执行,这个过程以此反复进行,直到执行队列中的任务被执行完毕。

2.JS执行栈可以说是JS引擎的主线程,任务队列则是其他异步线程的任务合并集,各个异步线程对任务队列执行入队操作,然后由浏览器的事件轮循(Event Loop)做出队操作(添加到JS主线程中执行)。任务队列是FIFO的数据结构。

3.事件轮循(Event Loop)就是JS主线程循环获取执行队列中的任务,并在JS主线程为空的时候会检查执行队列是否有任务需要执行,并且以定时检查的方式对任务队列进行循环检查。

写之前想写一些由UI多线程和JS单线程产生的示例来加以说明,但是这又会涉及到比较多的其他知识点,比如事件的监听、网络请求、定时器等相关的问题特别多,所以这部分到时候在具体的博客中来剖析我觉得更合适,所这篇博客就是一个非常抽象的原理剖析了,如果有不准确的地方还请指出,当然如果有不能理解的地方也可以给我留言。

最后就开篇提到的jQuery动画函数animate的核心机制问题,其本质就是动画函数内部采用了定时器来实现动画,也就是定时器的异步线程,所以当队列中添加第一个队列就可以马上对队列执行出队操作,而动画函数进入异步状态,所以动画函数会往下执行,将后面的所有链式调用的动画函数的动画都添加到队列中,然后动画队列就可以用next()来直接实现下一个动画出队操作了。这是一个非常经典的队列与定时器异步机制的综合应用。

浏览器UI多线程及JavaScript单线程运行机制的理解的更多相关文章

  1. JavaScript的运行机制

    先来看一段代码然后再来详细的说明js的运行机制,下面的一段代码执行顺序是什么 console.log(1); setTimeout(function () { console.log(2); }, 0 ...

  2. 简述JavaScript的运行机制

    想要理解JavaScript的运行机制,需要分别深刻理解以下几个点: · JavaScript的单线程机制 · 任务队列(同步任务和异步任务) · 事件和回调函数 · 定时器 · Event Loop ...

  3. 浅谈javascript的运行机制

    积累一下这几天学的,记录一下: 一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程 ...

  4. 我对asp.net运行机制的理解

    有一本书叫<asp.net 本质论>,作者名叫冠军.我看了几次,都没有坚持看下来.这次连续看完了前三章,然后参考网上的资料,总结下我对.net运行机制的理解. 先上两张图: 看着这两张图, ...

  5. [转]JavaScript线程运行机制

    从开始接触js时,我们便知道js是单线程的.单线程,异步,同步,互调,阻塞等.在实际写js的时候,我们都会用到ajax,不管是原生的实现,还是借助jQuery等工具库实现,我们都知道,ajax可以实现 ...

  6. 用一个小例子来谈谈javascript的运行机制

    先上例子! <script type="text/javascript"> console.log('博'); setTimeout(function(){ conso ...

  7. setTimeout运行机制简要理解

    经典例子辅助理解setTimeout工作原理 运行结果: 约1秒后输出:1,再过约1秒后输出:2,接着才立即输出:时间流逝了: 2002 毫秒最后输出:时间又流逝了: 2003 毫秒 在现有浏览器环境 ...

  8. 浏览器多进程架构、浏览器内核多线程、js单线程、GUI 渲染线程 与 JavaScript引擎线程互斥 原理

    浏览器是多进程的,有一个主控进程,以及每一个tab页面都会新开一个进程(某些情况下多个tab会合并进程). 出处:http://www.imweb.io/topic/58e3bfa845e5c1346 ...

  9. 浏览器进程/线程模型及JS运行机制

    浏览器是多进程的,有一个主控进程,以及每一个tab页面都会新开一个进程(某些情况下多个tab会合并进程). 进程可能包括主控进程,插件进程,GPU,tab页(浏览器内核)等等. Browser进程:浏 ...

随机推荐

  1. 雨后清风教你如何在Windows 7中对硬盘进行分区

    磁盘分区是将硬盘驱动器分成多个逻辑单元.人们通常不会选择对硬盘进行分区,但它有很多好处.主要是,通过对磁盘进行分区,您可以将操作系统与数据分开,从而减少数据损坏的可能性. 磁盘分区方法 打开“计算机管 ...

  2. 虚拟机硬盘vmdk压缩瘦身并挂载到VirtualBox

    这个问题其实困扰了挺久的,一直没闲情去解决,网上搜索过很多压缩方法感觉都太麻烦太复杂,因最近在windows上搞docker就一并解决了. 压缩vmdk 首先下载DiskGenius,这工具很牛X,相 ...

  3. VMware实现iptables NAT及端口映射

    1. 前言 本文只讲解实战应用,不会涉及原理讲解.如果想要了解iptables的工作流程或原理可参考如下博文. 具体操作是在PC机的VMware虚拟机上进行的,因此涉及的地址都是内网IP.在实际工作中 ...

  4. Linux上修改主机名

    依次执行以下命令 hostnamectl set-hostname 你想设置的名字 hostname 你想设置的名字(和上面的名字保持一致) exit 然后重新连接就行了

  5. C#基础知识之反射

    http://blog.csdn.net/educast/article/details/2894892 两个现实中的例子:1.B超:大家体检的时候大概都做过B超吧,B超可以透过肚皮探测到你内脏的生理 ...

  6. CentOS 7 增加磁盘分区挂载(lvm)

    1.查看主机现有磁盘情况 # fdisk -l 现在主机中存在一块8G的磁盘sdb,尚未分区挂载,所以需将磁盘进行分区挂载. 2.对磁盘进行分区 # fdisk /dev/sdb   (选择要操作分区 ...

  7. 初识服务发现及Consul框架的简单使用

    初识服务发现及Consul框架的简单使用   1.什么是服务发现? 服务发现组件记录了(大规模)分布式系统中所有服务的信息,人们或者其它服务可以据此找到这些服务. DNS 就是一个简单的例子. 当然, ...

  8. git 入门(转)

    1. good https://github.com/521xueweihan/git-tips Git的奇技淫巧 2.如果之前未使用过 Git,可以学习 Git 小白教程入门 3.思维导图

  9. MyExceptionFilter 异常注入

    public class MyExceptionFilter : IExceptionFilter { private ILogService logService; public MyExcepti ...

  10. EasyUI datagrid formatter 属性

    easyui的formatter属性可以帮助我们更加灵活的显示数据库中的数据. 比如,我有一个启用禁用字段,使用数字表示,1表示启用,2表示禁用,展示给客户的时候我当然希望是中文的形式. 只需要写这么 ...