从浏览器进程到Event Loop

104 阅读4分钟

1.浏览器的进程

1.1浏览器的渲染进程

包括GUI线程、JS引擎线程、定时器触发线程、事件触发线程、http异步请求线程。

js是单线程的脚本语言,也就是在一段时间内只能做事情。那么如何在一段时间内协调好事件、交互、样式、请求等行为,防止主线程阻塞,js的设计者就提出了event loop事件循环机制。

2.如何实现异步

上文所讲,js是一种单线程的脚本语言。那么单线程如何实现异步呢?先来了解几个基本概念。

2.1执行上下文

执行上下文包括全局执行上下文、函数执行上下文、eval函数执行上下文。

执行上下文的生命周期包括创建-执行-回收。执行上下文可看作栈,先进后出,后进先出。由于js代码运行时,最先进入的是全局执行上下文,因此处于栈底的永远是全局执行上下文,栈顶的是当前正在执行函数的执行上下文。

全局执行上下文:所有不在函数代码块中的代码,都位于全局执行上下文中。全局执行上下文只有一个,例如浏览器的执行上下文就是window对象,this指向这个全局对象。

函数执行上下文:顾名思义,处于函数体中。只有调用该函数时,才会为该函数创建一个新的执行上下文。函数执行上下文可以有无数个。

eval函数执行上下文:运行在eval函数中的代码,很少用。

执行上下文具体可看这位大佬的博文:amberzqx.com/2020/02/04/…

const firstFunction = () => {
    console.log('1');
    secondFunction();
    console.log('2');
}
const secondFunction = () => {
    console.log('3');
}
firstFunction();
console.log(4)

// 1324
//从上到下的执行
//从上到下执行,先是全局作用域,那就是栈底第一个
//firstFunction的调用,打印出1,现在栈顶是secondFunction,因为函数里面还没有执行完,所以还没有被销毁
//secondFunction的调用,打印3,secondFunction,因为函数里面执行完,所以要被销毁
//再执行栈顶firstFunction里面的2
//最后执行全局作用域中的4

2.2 js任务

同步任务,是指在主线程上排队执行的任务,只有上一个任务执行完毕,才会进行下一个任务;异步任务,是指不进入主线程,而进入任务队列中的任务。只有任务队列通知主线程,某个异步任务可以执行,该异步任务才会进入主线程中执行。

上述任务的解释,串起来讲,也就是event loop。具体见第三部分。

2.3宏任务和微任务

宏任务可以理解为,每次执行栈执行的代码就是一个宏任务,每个宏任务都是添加到任务队列中执行的。因此“任务队列”又称“宏任务队列”。常见的宏任务有:整体的script、setTimeOut、setTimeInterval、setImmediate

微任务是指当前宏任务执行完成后,立即执行的任务。常见的微任务有:Promise、process.nextTick

**! Caution:**Promise新建后立即执行,也就是说,new Promise构造函数的一个宏任务,但promise注册的.then回调和.catch回调函数是微任务。

**! Important:**每次事件循环,都是先执行宏任务,再执行宏任务代码块里面的微任务。也就是每个事件循环只会执行一个宏任务。

每次事件循环称为一个tick,每次tick先执行宏任务,当遇到宏任务中有微任务时,将微任务放入微任务队列中。宏任务执行完毕后,再依次去执行已被放入微任务队列中的微任务。如此循环。

(宏任务和微任务有很多代码执行的例子,例如下面这种,先打印出什么,再打印出什么 -.- 例子很多,可自行百度)

console.log('1')
setTimeOut(() => {
    console.log('2')
}, 0)
Promise.resolve().then(() => {
    console.log('3')
}).then(() => {
    console.log('4')
})
console.log('5')
// 1 5 3 4 2
//第一轮循环,执行宏任务script里面的同步任务,遇到微任务挂起,先执行1、5
//执行完宏任务,执行微任务3、4 ,微任务执行完毕了就是下一轮事件循环的开始
//第二轮循环,执行setTimeOut里面的2

3.eventloop原理

event loop本质上是一种事件循环机制 。所有的同步任务都在主线程上执行,形成一个执行栈;主线程之外,还存在一个任务队列,只要异步任务有了运行结果,就由任务队列通知主线程,在主线程中放置一个事件;一旦执行栈中的所有同步任务都执行完毕,系统就开始读取任务队列钟会的异步任务,此时异步任务就可以结束等待状态,进入执行栈,开始执行;上述过程不断循环,就形成了event loop。