likes
comments
collection
share

事件循环到底是什么回事啊?

作者站长头像
站长
· 阅读数 22

事件循环机制使js能够非阻塞的执行代码,处理异步操作

本文会从浏览器的进程和线程开始,逐渐讲到事件轮询机制。

事件循环到底是什么回事啊?

浏览器的进程和线程

众所周知,浏览器是个多进程应用,本文要讲的事件循环机制主要涉及的就是浏览器渲染进程,其包括多线程:

  1. GUI渲染线程:主要负责页面的渲染,其与js引擎呈互斥关系
  2. js引擎线程:主要负责解析js文件,是本文的主角之一
  3. 事件触发线程:当异步事件执行完后,由该线程负责将其触发的回调函数根据异步源(宏任务还是微任务),放入对应的任务队列中
  4. 定时触发线程:顾名思义该线程主要负责执行定时器任务
  5. 异步http请求线程:该线程主要负责http请求相关任务

js是单线程的

js是单线程的说的就是其只有一个执行线程,也就是js引擎线程。因为在浏览器环境中,js可以对dom进行操作,如果其设计为多线程,当多个线程同时对dom进行操作,很容易造成冲突。

js单线程存在的问题

单线程的意思是说每次执能执行一个任务,就像高速收费口一样,每次执能处理一辆车的缴费工作,如果这辆车缴费过程很慢,那么后面车就只能等着。如果没有任务队列和事件循环机制,那么js的单线程也会存在同样的问题,当遇到例如定时器这样需要长时间等待执行的函数,那么就会阻塞后面函数的执行。

但好在这种情况并不会发生,浏览器通过任务队列配合事件循环机制,非常巧妙的实现了js非阻塞执行。

任务队列

队列是一种尾进头出,先进先出的数据结构。任务队列中存放的是异步函数执行完后触发的回调函数。当执行栈为空后,主线程会来任务队列里查看是否有待执行的回调函数,如果有则将其放入主执行栈中进行执行。但这里有个关键细节,主线程会先检查微任务队列中的任务,直到微任务队列清空,才会去执行宏任务队列,每当执行完一个宏任务,会再次检查微任务队列,这个过程会一直重复形成循环。也因此有多个宏任务队列,而微任务队列只有一个。

是的没错,任务队列分为宏任务队列和微任务队列。对于不同的异步任务其是存在优先级的,微任务队列中存放优先级高的微任务,宏任务队列中存放优先级低的宏任务。这样做的目的其实就是为了‘插队’,当来了一个优先级比较高的,但任务队列中却排满了异步任务,再将其放到最后排队显然是不合理的。

那么有哪些异步函数是微任务,哪些又是宏任务呢?我自己通俗理解,微任务一般是js自身带的api,宏任务则是运行环境如浏览器带的api。

宏任务

  • setTimeout,setInterval
  • script文件的执行
  • i/o操作:网络请求等
  • ui渲染

微任务

  • Promise
  • async/await
  • MutationObserver

整体流程

  1. js从宏任务队列出取出一个宏任务(最开始就是运行script文件)

  2. 遇到宏任务将其交给对应的线程进行处理,处理完后会触发回调函数,将回调函数存入宏任务队列。

    遇到微任务直接将微任务存入微任务队列

  3. 待执行栈被清空后,检查微任务队列中是否有微任务,如果有则将其按顺序调入执行栈进行执行,直至全部执行完

  4. 再取出一个宏任务

  5. ....

  6. 按照上述顺序全部执行完

涉及概念

执行栈 :我们知道 js 执行的就是一个个的函数,每个函数都有其对应的执行上下文,这其中包括 作用域 ,this等,每当浏览器执行一个函数,就会将其对应的执行上下文压入执行栈中,运行完后再将其弹出。

回调函数 :当遇到异步任务时,我们不能知道其会在什么时候执行结束,比如一个 http请求 我无法知道其什么时候会返回结果。回调函数的出现就解决了这个问题,将回调函数作为参数传给异步任务,等其执行结束后会触发这个回调函数。

转载自:https://juejin.cn/post/7393314542094204938
评论
请登录