美文网首页
JavaScript运行机制总结

JavaScript运行机制总结

作者: surlyyoung | 来源:发表于2019-03-22 18:31 被阅读0次

JavaScript运行机制总结


文章摘抄总结至 此处,只用于个人学习啊啊啊啊啊啊,侵权删


从浏览器说起


1.浏览器是多进程的

2.浏览器的每一个tab页面相当与一个独立的浏览器进程,某些情况tab页面可能被浏览器合并

3.浏览器包括的进程与多进程优点如下图所示:

图1-1 浏览器的多线程及其优点

渲染进程(Renderer进程)


1.  页面的渲染,JS的执行,事件的循环,都要集中在 渲染进程 

2.  渲染进程是多线程的

3.  渲染进程的常驻线程及其作用如下图所示:

图1-2 渲染进程的常驻线程及其作用

浏览器主进程(Browser进程)与渲染进程的通信


如下图所示:

图1-3 浏览器主进程(Browser进程)与渲染进程通信流程

涉及到的进程:

图1-4 浏览器渲染时进程之间的通信

渲染进程中的线程


1. GUI渲染线程与JS引擎线程互斥

图1-2 渲染进程的常驻线程及其作用  中提到 GUI渲染线程与JS引擎线程互斥,因为JavaScript是可以操作DOM的元素的(DOM提供了接口),同时操作DOM和渲染页面是不大现实的,因为数据不一致。故在JS引擎执行时,GUI渲染线程是被挂起的,待JS引擎空闲时恢复。故这一机制会有如下问题。

2. JS会阻塞页面的加载

由于 GUI渲染线程与JS引擎线程互斥 ,JS引擎在巨量运算时,GUI渲染线程只能干等,在用户上看就有卡顿的感觉,页面渲染不连贯。

3. Web Worker

创建Worker时,JS引擎向浏览器申请开一个子线程(子线程是浏览器开的,完全受主线程控制,而且不能操作DOM);JS引擎线程与worker线程间通过特定的方式通信(postMessage API,需要通过序列化对象来与线程交互特定的数据)。

综上,Web Worker是另开一个线程,用于解决计算量大的问题,而JS引擎依旧是单线程

另外 SharedWorker 与 WebWorker 本质上就是进程和线程的区别,haredWorker由独立的进程管理,WebWorker只是属于render进程下的一个线程

浏览器渲染流程


浏览器在渲染过程中,主要涉及到 渲染进程的GUI渲染线程。参考下面图片:

图1-5 浏览器渲染页面流程图

JS运行机制与Event Loop


1. JS分为同步任务和异步任务

2. 同步任务都在 主线程(JS引擎线程) 上执行,形成一个 执行栈

3. 主线程之外,事件触发线程 管理着一个任务队列,只要异步任务(如定时器触发线程计时完毕等)有了运行结果,就在任务队列之中放置一个事件。(这里的回调函数还涉及到 定时触发器线程 和 异步http请求线程 的回调函数

4. 一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行

如下图:

图1-6 JS事件循环图 图1-7 JS事件循环图2

故JS的事件循环(Event Loop)过程是:

1. 主线程(JS引擎线程)运行时会产生执行栈栈中的代码调用某些api(上图所示)时,当满足触发条件后,(如ajax请求完毕有回调函数)它们会在事件队列中添加各种事件

2. 当 栈中的代码执行完毕,就会读取(队列头部开始)事件队列中的事件,放入执行栈中,去执行那些回调

3. 重复1,2

综上,JS在运行的时候主要涉及到 渲染进程的两个线程——JS引擎线程 和 事件触发线程,另外,在执行代码时,可能会触发到 定时器触发线程 以及 异步http请求线程。

事件循环的另一个角度:macrotask与microtask


原因:在ES6之后,Promise里有了一个新的概念:microtask

如此一来,JS中分为两种任务类型:macrotask和microtask,在ECMAScript中,microtask称为jobs,macrotask可称为task。

macrotask: 

1. macrotask(又称之为宏任务),可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)

2. 浏览器为了能够使得JS内部task与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->...)

microtask: 

1. microtask(又称为微任务),可以理解是在当前 task 执行结束后立即执行的任务

2. 也就是说,在当前task任务后,下一个task之前,在渲染之前执行

3. 在某一个macrotask(宏任务)执行完后,就会将在它执行期间产生的所有microtask(微任务)都执行完毕(在渲染前)

4. 微任务有微任务队列(Job Queues)

宏任务与微任务分类:

macrotask:主代码块,setTimeout,setInterval,setImmediate,MessageChannel (优先级先于setTimeout)等(可以看到,事件队列中的每一个事件都是一个macrotask)

microtask:Promise,process.nextTick,MutationObserver(优先级最小)等

另外,setImmediate是有规定:在下一次Event Loop(宏任务)时触发(所以它是属于优先级较高的宏任务), (Node.js文档中称,setImmediate指定的回调函数,总是排在setTimeout前面

综上,一次的Event Loop(事件循环) 的运行机制就是以下几个步骤:

1. 执行一个宏任务(第一次肯定是栈中的代码块,没有就从事件队列中获取)

2. 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中

3. 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(当没有优先级时依次执行)

4. 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染

5. 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

如下图所示:

图1-8 事件循环之宏任务与微任务图

注:有一些浏览器执行结果不一样(因为它们可能把microtask当成macrotask来执行了), 但是为了简单,这里不描述一些不标准的浏览器下的场景(但记住,有些浏览器可能并不标准)。另外,Promise的polyfill与官方版本的实现不同。polyfill,一般都是通过setTimeout模拟的,所以是macrotask形式。

参考文章:

1. http://www.dailichun.com/2018/01/21/js_singlethread_eventloop.html

2. https://segmentfault.com/a/1190000017204460

相关文章

网友评论

      本文标题:JavaScript运行机制总结

      本文链接:https://www.haomeiwen.com/subject/xwnkvqtx.html