zoukankan      html  css  js  c++  java
  • 关于JavaScript的执行机制

    浏览器线程
    一般来讲,一个程序一个进程,例如:低版本IE。Chrome为提高速度和保证访问一个有问题的页面,页面奔溃不影响其他的页面,采用沙箱技术。是一个网页一个进程,这也是Chrome吃内存的原因所在)。
    浏览器包含有以下线程:
    GUI渲染线程
    • 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
    • GUI渲染线程与JavaScript引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起(被冻结),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
    JavaScript引擎线程(JavaScript内核)
    • 负责处理Javascript脚本程序(例如V8引擎)。
    • JavaScript引擎线程负责解析Javascript脚本,执行代码。
    • JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,一个标签页(进程)中无论什么时候都只有一个JavaScript线程在执行JavaScript代码。
    GUI渲染线程与JS引擎线程是互斥的,因为JavaScript脚本是可操纵DOM元素,在修改这些元素属性同时渲染界面,那么渲染线程前后获得的元素数据就可能不一致了。如果JavaScript执行的时间过长,会造成页面的渲染不连贯,导致页面渲染加载阻塞。
    事件触发线程
    • 归属于浏览器而不是JavaScript引擎,用来控制事件循环(浏览器另开线程协助JavaScript引擎)。
    • 当JavaScript引擎执行代码块如鼠标点击、键盘事件等,会将对应任务添加到事件线程中。
    • 当对应的事件符合触发条件被触发时,事件触发线程会把事件的回调函数添加到任务队列中,等待JS引擎空闲后执行。
    定时触发器线程
    • setInterval与setTimeout所在线程
    • 浏览器定时计数器并不是由JavaScript引擎计数的,因此通过单独线程来计时并触发定时。计时完毕后,定时触发器线程将回调函数添加到任务队列中,等待JS引擎空闲后执行
    • 注意,W3C规定要求setTimeout中低于4ms的时间间隔算为4ms。
    异步http请求线程
    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求
    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中,再由JavaScript引擎执行。
    JavaScript 引擎最大特点就是:单线程,同一个时间只能做一件事。
    JavaScript引擎自上而下依次执行代码,如果遇到一段代码特别花费时间,就会阻塞后面代码的执行,造成浏览器「假死」状态。
    因此,将任务分为:同步任务和异步任务。用一个执行机制来合理运行相关代码,这个机制就是:事件循环。
    事件循环 Event Loop

    图表述:
    • 同步任务和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table。
    • 当指定的事情完成时,Event Table会将这个事件注册的回调函数移入Event Queue。
    • 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
    • 上述过程会不断重复,就是Event Loop(事件循环)。
    同步任务
    在主线程上排队执行的任务,只有前一个任务执行完毕,后一个任务才可以执行。
    异步任务
    不直接进入JavaScript主线程,而进入eventTable。
    异步任务例子:
    setTimeout(() => console.log('我是异步任务的回调函数代码'), 5000)
    由定时器触发线程去计算这个5000ms,5000ms后,定时器触发线程将定时器中的回调函数
    () => console.log('我是异步任务的回调函数代码')
    放在任务队列中(事件队列/eventQueue),当JavaScript主线程执行完所有的同步任务后空闲时,主线程去任务队列读取回调函数,拿到JavaScript主线程中进行执行。
    那是怎么知道主线程执行栈为空呢?
    JavaScript引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里读取是否有等待被调用的函数。
    宏任务 macrotask
    定义:每次执行栈执行的代码就是一个宏任务(包括同步任务、每次从事件队列中获取一个事件回调并放到执行栈中执行)。
    浏览器为了能够使得JS内部macrotask与DOM任务能够有序的执行,会在一个task执行结束后,在下一个 macrotask 执行开始前,对页面进行重新渲染 (macrotask->渲染->macrotask->...) 鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析HTMl。
    宏任务包含:
    • 一片整体代码/script标签内整体的代码
    • setTimeout setInterval
    • UI交互事件
    • postMessage
    • MessageChannel
    • I/O 交互
    • setImmediate(Node.js 环境)
    setImmediate与setTimeout的区别
    在官方文档中的定义,setImmediate为一次Event Loop执行完毕后调用。
    setTimeout则是通过计算一个延迟时间后进行执行。
    但是同时还提到了如果在主进程中直接执行这两个操作,很难保证哪个会先触发。
    因为如果主进程中先注册了两个任务,然后执行的代码耗时超过XXs,而这时定时器已经处于可执行回调的状态了。
    所以会先执行定时器,而执行完定时器以后才是结束了一次Event Loop,这时才会执行setImmediate。
    setTimeout(() => console.log('setTimeout'))
    setImmediate(() => console.log('setImmediate'))
    // node环境里执行多次,结果会不同。
    微任务 microtask
    需要在当前 macrotask 执行结束后立即执行的任务,比如对一系列动作做出反馈,或者是需要异步的执行任务而又不需要分配一个新的task,这样便可以减小一点性能的开销。
    只要执行栈中没有其他的js代码正在执行且每个宏任务执行完,微任务队列会立即执行。如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。
    微任务包含:
    • Promise.then/catch/finally
    • async/await函数
    • Object.observe
    • MutaionObserver
    • process.nextTick(Node.js 环境)
    async/await本质上是基于Promise的一些封装,而Promise是属于微任务的一种。所以在使用await关键字与Promise.then效果类似。
    async函数在await之前的代码都是同步执行的,await之后的所有代码都是在Promise.then中的回调。
    setTimeout(() => console.log(4))
    async function fn() {
    console.log(1)
    await Promise.resolve()
    console.log(3)
    }
    fn()
    console.log(2)
    // 1 2 3 4
    process.nextTick
    含义:定义出一个动作,并且让这个动作在下一个事件轮询的时间点上执行。
    它是在本轮循环执行的,而且是所有异步任务里面最快执行的。
    可以用settimeout模拟效果,但是内部机制不同,不做深入研究。
    宏任务和微任务之间的关系图:

    在每一次的事件循环过程中关键步骤如下:
    • 执行一个宏任务(栈中没有就从事件队列中获取宏任务)
    • 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
    • 宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)
    • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
    • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)
    宏任务,微任务总结:
    • 宏任务按顺序执行,且浏览器在每个宏任务执行之间渲染页面。
    • 微任务也按顺序执行,且在以下场景会立即执行所有微任务。
    • 每个回调之后且js执行栈中为空。
    • 每个宏任务结束后。

    转至:https://www.yuque.com/liaoing/blog/wbx8cu

  • 相关阅读:
    leetcode链表--15、reverse-nodes-in-k-group(按照k值进行k个结点的逆序)
    4、消除重复元素--网易2017春招
    24、剑指offer--二叉树中和为某一值的路径
    leetcode链表--14、add-two-numbers(两链表相加 得到新链表)
    3、调整队形--网易2017春招
    2、赶去公司--网易2017春招
    1、双核处理--网易2017春招
    CSS3自定义滚动条样式 -webkit-scrollbar
    git安装使用
    div+css居中
  • 原文地址:https://www.cnblogs.com/bomdeyada/p/14388929.html
Copyright © 2011-2022 走看看