浏览器UI多线程及JavaScript单线程运行机制的理解-程序员宅基地

技术标签: ViewUI  ui  数据结构与算法  javascript  

在上一篇博客中,我对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()来直接实现下一个动画出队操作了。这是一个非常经典的队列与定时器异步机制的综合应用。

转载于:https://www.cnblogs.com/ZheOneAndOnly/p/10417280.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30608503/article/details/95302159

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法