新野微网站建设,上海网页制作模板,企业seo年度,慈溪网站制作浏览器与js运行机制
进程与线程
进程
进程是CPU分配资源的最小单位#xff0c;它是一个可以自己独立运行且拥有自己资源空间的任务程序#xff1b;包括程序以及程序所使用的内存及系统资源
线程
线程是CPU调度的最小单位#xff0c;它就是程序中的一个执行流#xff1…浏览器与js运行机制
进程与线程
进程
进程是CPU分配资源的最小单位它是一个可以自己独立运行且拥有自己资源空间的任务程序包括程序以及程序所使用的内存及系统资源
线程
线程是CPU调度的最小单位它就是程序中的一个执行流也可以理解为一个进程代码的不同执行路径
一个进程中只有一个执行流就是单线程程序按照顺序执行前面的处理好才执行后面的
一个进程中有多个执行流就是多线程多个线程并行执行各自的任务
JS为什么是单线程
多线程的复杂性多线程操作需要加锁编码的复杂性会增高。而且如果同时操作 DOM 在多线程不加锁的情况下最终会导致 DOM 渲染的结果不可预期
HTML5提出了 Web Worker标准允许JS脚本创建多个线程但是子线程完全收主线程控制而且不得操作DOM所以它并没改变JS单线程的本质
浏览器多线程
浏览器是多进程
我们没打开一个Tab标签页就会产生一个进程
如果我们打开多个Tab标签页其中一个Tab标签页崩溃了影响整个浏览器那体验肯定是不行的所以不可能是单进程每个进程有多个线程都会占用资源所以我们打开多个标签页可能会卡谷歌就有标签页限制
浏览器有那些进程
Broswer进程
浏览器的主进程该进程只有一个主要是一个协调、主控的作用
负责浏览器的页面展示、交互前进后退
负责页面的管理创建和销毁其他进程
网络资源的管理下载等
第三方插件进程
使用插件是才创建一个插件对应一个进程
GPU进程
该进程只有一个负责3D的绘制等
Render渲染进程
渲染进程就是我们所说的浏览器内核内部是多线程
每个tab页面都有一个渲染进程
主要作用是页面渲染、脚本执行事件处理等
Render进程及它主要的线程
render进程是多线程
GUI渲染线程
主要负责页面的渲染解析html、css,生成DOM树、CSS规则树构建Render树页面的布局绘制
JS引擎线程
JS引擎线程比如V8引擎是JS内核负责解析JavaScript脚本运行代码
一个render进程中无论什么时候都只有一个JS线程再运行JS程序
JS引擎线程与GUI渲染线程互斥
因为JS引擎可以修改DOM树那么如果JS引擎在执行修改了DOM结构的同时GUI线程也在渲染页面那么这样就会导致渲染线程获取的DOM的元素信息可能与JS引擎操作DOM后的结果不一致。
当JS引擎执行的时候GUI线程需要被冻结但是GUI的渲染会被保存在一个队列当中等待JS引擎空闲的时候执行渲染
如果JS引擎正在进行CPU密集型计算那么JS引擎将会阻塞长时间不空闲导致渲染进程一直不能执行渲染页面就会看起来卡顿卡顿的渲染不连贯。所以要尽量避免JS执行时间过长。
事件触发线程
属于浏览器而不是JS引擎
用来控制事件循环管理事件队列
当js执行碰到事件绑定和异步操作会走事件触发线程将对应的事件添加到对用的线程中当事件触发或异步有了结果将它们的回调事件添加到事件队列等待JS引擎事件线程处理
因为JS是单线程所以事件队列中的事件都要等待JS引擎线程处理
定时触发线程
浏览器定计数器不是在 JS 引擎线程中计数的JS引擎是单线程如果处于阻塞线程状态就计不了时,JS引擎线程与GUI渲染线程互斥GUI进程执行的时候就阻塞了JS引擎线程就记不了时了
计时完成后会添加到事件触发线程的事件队列中
W3C在HTML标准中规定规定要求setTimeout中低于4ms的时间间隔算为4ms
异步http请求线程
当执行到一个http异步请求时就把异步请求事件添加到异步请求线程等收到响应(准确来说应该是http状态变化)再把回调函数添加到事件队列等待js引擎线程来执行
事件循环机制Event Loop
JS分为同步任务和异步任务同步任务在主线程也就是JS引擎线程上执行
除了主线程之外我们的事件触发线程中有一个任务队列只要异步事件有了结果就会在任务队列总添加它的回调事件任务队列中由两个队列一个宏任务队列一个时微任务队列
1》首先我们的同步任务会进入主执行栈异步任务交给事件触发线程异步任务有了结果才会被添加到事件队列中
2》当我们主执行栈中的代码执行之后会先去微任务队列读取任务然后执行
3》执行完所有的微任务后又会到微任务队列中读取微任务因为刚才可能产生了新的微任务直到没有读取到微任务然后GUI渲染线程会进行一次渲染会阻塞js执行
4》渲染后会去宏任务队列读取宏任务然后执行
5》宏任务执行完毕会去微任务队列读取任务有就执行然后和之前一样读取微任务直到没有微任务进行渲染一个事件循环结束
6》渲染页面再读取、执行宏任务之前执行完所有微任务之后渲染一次页面
6》重复上面的事情微任务-渲染-宏任务 宏任务
所有微任务执行完后下一个宏任务执行前GUI渲染线程会渲染一次页面
常见宏任务
setTimeout
setInterval
requestAnimationFrame(浏览器)
微任务
常见微任务
Promise.then()
Promise.catch()
Promise.finally()
process.nextTick (node)
Object.observe
this.$nextTick()
Vue异步执行DOM更新。只要观察到数据变化Vue将开启一个队列并缓冲在同一事件循环中发生的所有数据改变。如果同一个watcher被多次触发只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。然后在下一个的事件循环“tick”中Vue刷新队列并执行实际 (已去重的) 工作。
nextTick会创建一个微任务或宏任务将其内部回调推入微任务队列中vue中一个事件循环中所有dom更新也是一个微任务dom更新在这个微任务之前进入微任务队列中是先更新dom再执行this.$next中的代码所以可以再里面获取到更新后的dom
nextTick异步处理更新队列的逻辑在下一个的事件循环“tick”中去刷新队列依次尝试使用原生的 Promise.then、MutationObserver 和 setImmediate如果执行环境都不支持则会采用 setTimeout(fn, 0) 代替所以this.$nextTick(vue中) 可能是一个微任务也可能时宏任务
我们再来梳理一遍上面从数据变更到 dom 更新之前的整个流程
修改响应式数据触发 Object.defineProperty 中的 set发布通知触发 Watcher 中的 update 方法update 方法中把 Watcher 缓冲到一个队列刷新队列的方法(其实就是更新 dom 的方法)传到 nextTick 方法中nextTick 方法中把传进来的 callback 都放在一个数组 callbacks 中然后放在异步队列中去执行
然后这时你调用了 $nextTick 方法传进来一个获取最新 dom 的回调这个回调也会推到那个数组 callbacks 中此时遍历 callbacks 并执行所有回调的动作已经放到了异步队列中到这假设你后面没有其他的代码了所有的同步代码就执行完了然后开始执行异步队列中的任务更新 dom 的方法是最先被推进去的所以就先执行你传进来的获取最新 dom 的回调是最后传进来的所以最后执行显而易见当执行到你的回调的时候前面更新 dom 的动作都已经完成了所以现在你的回调就能获取到最新的 dom 了。