事件循环和垃圾回收

175 阅读4分钟

如果你对事件循环还是不太了解,快来和我一起学习一下吧~~~

在浏览器中包含了许多的进程。

  • 主进程
  • GPU进程
  • 第三方插件进程
  • 渲染进程(浏览器内核)

其中我们最关注的是渲染进程,主要负责页面的渲染,脚本执行,事件处理等

渲染进程中又包含了许多的线程

  • GUI渲染线程
    • 负责页面渲染,布局和绘制,当页面需要重排和重绘时,就会执行
    • 与js引擎线程互斥,这样可以防止在渲染的时候操作dom导致渲染不可预期
  • JS引擎线程
    • JS引擎线程是一个单线程的,与GUI渲染进程互斥
    • 主要负责解析和执行js脚本
  • 事件触发线程(管理事件队列)
    • 当事件触发的时候,会将事件放入到js所在执行队列中,比如点击事件等
  • 定时器触发线程
    • settimeout 和setInterval由该线程计时,计时完毕后,通知事件触发线程
  • 异步请求线程
    • 用来处理ajax 请求,请求完成后,通知事件触发线程

Js 是单线程的, 因为涉及到dom操作,多线程需要进行同步判断,单线程降低了复杂度

但是因为是单线程,所以当遇到定时器,网络请求的时候就会阻塞线程,所以js 将代码分为了一个个任务,Js 分为同步任务和异步任务,任务在js引擎上执行,当遇到同步任务的时候,先执行,形成一个执行栈,当遇到异步任务的时候,就会根据不同的异步任务让不同的线程执行,当异步任务的触发条件达成后,会将回调事件放到由事件触发线程管理的任务队列中(任务队列有两个,一个宏任务队列,一个微任务队列), 当执行栈中的同步任务执行完成后,若微任务队列中有,则清空微任务,若微任务队列中没有回调,就会从任务队列(宏任务队列)中取出一个异步任务的回调加入到执行栈中开始执行。(异步的回调任务中也可能有同步任务,其他异步任务,就还是会按照上面这个顺序进行)

  • js 引擎线程只会执行执行栈中的事件
  • 执行栈中的代码执行完毕,就会读取事件队列中的事件
  • 事件队列中的回调事件,是由各自线程插入到事件队列中的
  • 如此循环

微任务队列是为了让异步任务有优先级的概念,于是又设计了一个高优先级的队列(微任务队列),每次执行完普通宏任务后,就去把所有的高优先级的任务执行一遍,清空微任务队列,然后执行下一个宏任务

JS引擎线程 和负责页面渲染的GUI线程互斥,为了让js任务和dom渲染任务有序进行,每当一个宏任务执行完后,检查微任务队列中有没有,有就执行清空微任务队列,然后就会由GUI线程进行渲染,渲染完成后,然后执行下一个宏任务。

同步任务1和同步任务2执行过程中产生了微任务的执行流程,且第一个宏任务中产生了微任务

同步任务1-->渲染-->同步任务2-->渲染-->微任务-->渲染-->宏任务-->微任务-->渲染-->宏任务-->渲染

同步任务1和同步任务2中没有产生微任务的流程,且第一个宏任务中产生了微任务

同步任务1-->渲染-->同步任务2-->渲染-->宏任务-->微任务-->渲染-->宏任务-->渲染

任务分类

  • 同步任务

  • 宏任务 定时器回调,ajax 会调, dom事件回调

  • 微任务 Promise.then回调 mutationObserver 回调

总结:

代码执行的是,遇到同步任务直接执行,遇到异步任务就会根据任务类型分别放到宏任务队列和微任务队列,同步任务执行完后,查看微任务队列,如果有微任务,则保证把所有的微任务全部执行完(包括执行微任务中产生的新的微任务), 然后去执行查看宏任务队列,执行下一个宏任务,如果在执行宏队列的时候,产生了新的微任务,则执行完微任务再去接着执行宏队列 ,执行完后,查看微任务队列,重复循环,直到宏任务队列为空。

垃圾回收机制

标记清理

当申明一个变量的时候,这个变量会被加上存在于上下文中的标记,当变量离开上下文时候,也会被加上离开上下文的标记

引用计数

申明变量并给他赋值一个引用值,引用数为1,如果其他值又有引用到,则引用数加一