在上一篇博客中,我对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. C 语言 IO 缓存 相关

    必要了解函数的功能和使用场景: fflush, setbuf, setvbuf 了解的操作: setbuf(stdout,NULL); // 关闭输出缓冲区: libc 和 linux 内核IO缓存模 ...

  2. 【原创】Windows平台下Git的安装与配置

    一.下载     msysgit是Git for Windows版,其Home Page为:http://msysgit.github.io/ 点击页面中“Download”进入下载列表.可根据个人喜 ...

  3. swoole多端口监听

    今天测试swoole写webserver实现多端口监听.记录下爬过的坑:关于tcp协议监听触发不到receive!!!!! 首先上服务端代码 class Http { /** * 服务实例 * @va ...

  4. localStorage和sessionStorage数据存储

    var arr=[]; for(var i=0;i<4;i++){ arr[i]=i+i; } console.log(arr.toString()); //将json数据转化为字符串 var ...

  5. DeconvNet 论文阅读理解

    学习语义分割反卷积网络DeconvNet 一点想法:反卷积网络就是基于FCN改进了上采样层,用到了反池化和反卷积操作,参数量2亿多,非常大,segnet把两个全连接层去掉,效果也能很好,显著减少了参数 ...

  6. VS2017 安装Swagger初步认识

    1.安装NuGet包 2.配置 3.运行测试 参考博客:https://www.cnblogs.com/yilezhu/p/9241261.html 一 安装NuGet包 包名:Swashbuckle ...

  7. KEIL_MDK生成Bin文件

    1.MDK配置 MDK是使用安装目录下的(formelf.exe)工具来生成bin文件,配置方法:勾选 "Run # 1",在后面输入框写入bin文件生成方式 2.绝对路径 &qu ...

  8. git方法 GUI here

    注:stage changed是将所有修改归集到一次commit,如果要分开commit,则应该使用ctrl+t来一个一个文件的stage

  9. mybatis 使用auto mapping原理实现表间关联

    Auto mapping的示例 数据库中有一个person表,结构如下: mysql> desc person; +-------+-------------+------+-----+---- ...

  10. C#while死循环时候cpu占用比例大

    C#while死循环时候cpu占用比例大 原因:线程等不到释放,windows运行模式是抢占资源 解决方法:休眠一毫秒让垃圾回收可以进来回收资源 while (true) { Thread.Sleep ...