深入理解 JavaScript 代码执行机制
深入理解 JavaScript 代码执行机制
前言
本文仅为个人见解,如有错误的地方欢迎留言区探讨和指正。
1、餐前甜品
如下是一段 JavaScript 代码,如果你毫不犹豫的说出代码执行顺序。那么请直接滚动到底部,留下你的足迹,接受膜拜。如果还不是很确定,那么请往下继续查看。
let element = document.querySelector('.test');
num = 0,
start = null;
function step(timestamp) {
console.log('requestAnimationFrame');
if (!start) start = timestamp;
var progress = timestamp - start;
const p = new Promise((resolve, reject) => {
resolve('then');
}).then(() => {
console.log('promise1111');
});
element.style.left = Math.min(progress / 10, 200) + 'px';
if (progress < 3000) {
window.requestAnimationFrame(step);
}
}
document.body.onscroll = function(ev) {
console.log('我被滚动了');
}
window.requestAnimationFrame(step);
// 试试将 16改成18,在看看打印的代码顺序
setTimeout(() => {
console.log('setInterval');
}, 16);
const p = new Promise((resolve, reject) => {
console.log('promise');
resolve('then');
}).then(() => {
console.log('then');
});
2、磨刀不误砍柴工(了解浏览器原理)
(1) 进程和线程
- 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)
- 线程是cpu调度的最小单位(线程是建立在进程的基础上的一个程序运行单位,一个进程中可以有多个线程)
进程可以类比为工厂,线程就是工厂里面的工人,一个工厂可以包含一个或者多个工人,工人之间可以相互协作,并且共享工作空间
(2) 浏览器的多进程架构
现代的浏览器采用的都是多进程架构,主要包含以下三种进程:
1.Browser进程
浏览器的主线程,主要负责浏览器的页面管理、书签、前进后退、资源下载管理等,整个浏览器应用程序只有一个,对应上述浏览器组成中的浏览器引擎。
2.渲染进程
内核进程、负责页面渲染、JS执行,对应的是上述的渲染引擎和JS引擎,一个浏览器可以包含多个渲染进程,每个Tab窗口页对应一个渲染进程
3.GPU进程
负责GPU渲染,整个浏览器应用程序只有一个
4.插件进程
浏览器安装的插件(扩展程序),每个插件会创建一个进程
这种多进程浏览器架构主要有如下优势:
- 1.避免单个页面奔溃影响整个浏览器
- 2.避免第三方插件奔溃影响整个浏览器
- 3.充分利用多核优势
(3) 浏览器的渲染进程
- 浏览器有多个渲染进程、一个Tab页面一个(相同的Tab页面可能会被合并)
- 一个渲染进程包含多个线程
一个渲染进程主要包括如下线程:
- GUI线程(主要负责解析HTML、CSS和渲染页面),也就是人们常说的渲染引擎
- JS引擎线程(负责解析和执行JS代码)
- 事件线程(控制事件循环)
- 定时器线程(处理定时器相关逻辑)
- 异步请求线程(发起Ajax时会生成该线程)
线程规则:
- GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,页面的更新操作会等到JS引擎空闲时执行
- 一个渲染进程同时只有一个JS解析线程在运行
- JS引擎线程不停的处理事件线程推送到事件队列中的任务
- 定时器和异步请求最终生成的回调事件也有事件线程来控制和管理
常见浏览器的渲染引擎和JS引擎如下:
浏览器 | 渲染引擎 | JS引擎 |
---|---|---|
IE | Trident | Chakra |
Edge | EdgeHTML | Chakra |
Firefox | Gecko | SpiderMonkey |
Chrome | Webkit -> Blink | V8(著名的) |
Safri | Webkit | Javascriptcore |
Opera | Presto->Blink | Carakan |
了解了浏览器的渲染进程之后我们再来看看JS引擎。
3、正餐(JS引擎)
javascript 是单线程执行,且是 **逐帧 **执行。这个时候有人就要抬杠了,说webWorker 不是可以支持多线程么?没错,webworker 是可以开启子线程,但是此线程并不能操作dom。
js 代码执行分同步任务和异步任务,异步任务在当前同步任务队列执行完成后在依次执行。在 ES6中,又将异步任务分为了宏任务(macrotask)和微任务(microtask)。
ps: node 环境的任务不在此文讨论
(1) 宏任务
分类:
- setTimeout
- setInterval
- I/O
- UI交互事件
- postMessage
- MessageChannel
(2) 微任务
分类:
- Promise.then
- MutationObserver
(3) event-loop (任务事件执行顺序)
js 主线程先执行同步任务,遇见微任务和宏任务就将其交给事件线程处理,进行堆栈。当当前同步任务执行完成后,会读取当前的微任务队列。如果有,则依次执行。如果没有,则寻找是否有异步队列。如果有,则执行当前异步任务。如此反复,直至任务完成。
图解:
(4) requestAnimationFrame 是属于宏任务还是微任务?
其实,requestAnimationFrame 既不属于宏任务也不属于微任务。那么它在何时执行呢?答案就是:浏览器会在下一帧开始的时候把它加入到 微任务(microtask) 队列的最前方。
答案解析
所以上方的代码的代码解析就是 js 代码在每一帧的执行,永远是同步任务执行,然后在执行异步操作的时候,先清空微任务队列,然后在根据浏览器策略来判断 requestAnimationFrame 在哪一帧开始执行,最后清空宏任务队列。
最后,有兴趣的童鞋可以将上方的代码拷贝到本地,将 setTimeout 的数值改一下,然后会出现不一样的现象。有什么疑问欢迎在评论去留言,也欢迎吐槽。
上一篇: Python高级--决策树
下一篇: 2.设计模式-----策略模式
推荐阅读
-
深入理解 JavaScript 代码执行机制
-
【问底】陈焕生:深入理解Oracle 的并行执行
-
深入理解JavaScript创建对象的多种方式以及优缺点
-
深入理解Java中方法的参数传递机制
-
深入理解JavaScript系列(11) 执行上下文(Execution Contexts)_javascript技巧
-
JavaScript可否多线程? 深入理解JavaScript定时机制_javascript技巧
-
ORACLE锁机制深入理解
-
javascript onpropertychange事件疑惑,上全部代码,请指教,为什么它会在IEx下自动执行呢?
-
深入了解JavaScript的事件机制
-
this和执行上下文实现代码_javascript技巧