事件循环的基本过程
为什么使用事件循环
1.js 单线程
JavaScript 引擎中解析和执行 JavaScript 代码的线程只有一个(主线程),每次只能做一件事情
2.非阻塞
当js 执行异步任务(ajax/回调函数)时,会先将任务挂起,等结果返回后再执行;
如何实现挂起执行 ---事件循环机制
具体过程
js 变量存储
当 javascript 代码执行的时候会将不同的变量存于内存中的不同位置:堆(heap)和栈(stack)中来加以区分。其中,堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针
执行栈(call stack)
当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。context中包含上层、参数、this指向等信息;因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈; 例如
function fn() {
console.log('start')
fn1();
console.log('end')
}
function fn1() {
console.log('middle');
}
fn()
-
执行函数 fn()先入栈
-
fn()中先执行console.log('start') console.log('start') 入栈
-
console.log('start') 出栈
-
执行函数fn1(),fn1()入栈
-
执行console.log('middle');出栈
-
fn1()执行完毕, 出栈
-
执行 console.log('end'),入栈
-
console.log('end'),出栈
-
fn()执行完毕, 出栈
事件队列(callback queue)
js 引擎遇到一个异步事件后会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js 会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列;被放入事件队列不会立刻执行起回调,而是等待当前执行栈中所有任务都执行完毕,主线程空闲状态,主线程会去查找事件队列中是否有任务,如果有,则取出排在第一位的事件,并把这个事件对应的回调放到执行栈中,然后执行其中的同步代码
Eveent Loop 的循环过程
- 执行一个宏任务(一般一开始是整体代码(
script)),如果没有可选的宏任务,则直接处理微任务 - 执行过程中如果遇到微任务,就将它添加到微任务的任务队列中
- 执行过程中如果遇到宏任务,就将它添加到宏任务的任务队列中
- 执行一个宏任务完成之后,就需要检测微任务队列有没有需要执行的任务,有的话,全部执行,没有的话,进入下一步
- 检查渲染,然后
GUI线程接管渲染,进行浏览器渲染 - 渲染完毕后,JS线程继续接管,开始下一个宏任务...(循环上面的步骤)