浏览器内核
进程与线程
进程是系统分配的独立资源,是CPU资源分配的基本单位,进程是由一个或者多个线程组成的
线程是进程的执行流,是CPU调度和分配的基本单位,同个进程之中的多个线程之间共享该进程的资源
浏览器内核包括多个线程
- GUI渲染线程:
负责渲染页面,解析HTML,CSS,构建DOM树等,当页面重绘或者由于某种原因操作引起的回流都会调起该线程
和js引擎是互斥的,当js引擎线程在工作中的时候,GUI渲染线程会被挂起,GUI跟新被放入JS任务队列中,等待JS引擎线程空闲的时候继续执行
- JS引擎线程
单线程工作,负责解析运行JavaScript脚本和GUI渲染线程互斥,JS运行耗时过长就会导致页面阻塞
- 事件触发线程:
当时间符合触发条件被触发时,该线程会把对应的事件回调函数添加到任务队列的队尾,等待JS引擎处理
- 定时器触发线程:
浏览器定时器并不是由JS引擎计数的,阻塞会导致计时不准确 开启定时器触发线程来及时并触发计时。计算机完成后会被添加到任务队列中,等待JS引擎处理
- http 请求线程:
http请求的时候会开启一条请求线程,请求完成结束后,将请求的回调函数添加到任务队列中,等待JS引擎处理
事件循环 Event LOOP
简而言之,事件循环是按下面的几个步骤执行的
- 执行同步代码,
- 执行栈为空,查询是否有微任务需要执行(常见的微任务有: promise,process.nextTick)
- 执行所有微任务
- 去宏任务队列中拉去任务到执行栈中执行,(常见的宏任务有:script代码,setTimeout,setInterval)
- 必要的的话渲染UI
- 然后开始下一轮Event loop,执行宏任务中的异步代码
举例:
// 遇到 setTimeout 将 setTimeout 回调放入宏任务队列中
setTimeout(function () {
console.log(1)
}, 0)
// 遇到了 promise,但是并没有 then 方法回调
// 所以这句代码会在执行过程中进入我们当前的执行上下文 紧接着就出栈了
Promise.resolve(function () {
console.log(2)
})
// 遇到了一个 new Promise,Promise 有一个原则就是在初始化 Promise 的时候Promise 内部的构造器函数会立即执行,
// 因此在这里会立即输出一个 3,所以这个 3 是第一个输出的
new Promise(function (resolve) {
console.log(3)
})
// 然后第二个输出 4 当代码执行完毕后回去微任务队列查找有没有任务,
// 发现微任务队列是空的,那么就去宏仁务队列中查找,发现有一个我们刚刚放进去的setTimeout 回调函数,
// 那么就取出这个任务进行执行,所以紧接着输出1
console.log(4)
执行结果为3,4,1
注意注意 promise有无new的区别,new promise的时候内部的构造器函数会立刻执行,另外,我们所有的promise是微任务具体是指的是.then()方法返回的promise对象
console.log('begin'); // 1.begin
setTimeout(() => {
console.log('setTimeout 1'); // 3.setTimeout 1
Promise.resolve() // Promise.resolve().then :直接把 then 回调放入微任务队列
.then(() => {
console.log('promise 1'); // 5.promise 1
setTimeout(() => {
console.log('setTimeout2'); // 8. setTimeout2
});
})
.then(() => {
console.log('promise 2'); // 7. promise 2 注意7比8要快,then 方法返回一个新的 Promise 对象
});
new Promise(resolve => {
console.log('a'); // 4. a
resolve();
}).then(() => {
console.log('b'); // 6. b
});
}, 0);
console.log('end'); // 2.end