没有废话,只有干货,简单的概念就不说了,如果要先说事件循环,那么必须插一嘴,同步任务和异步任务,要说同步任务和异步任务,那么还的先说下 JS 单线程。
大家肯定都知道,JS 是一个单线程,如果设计为多线程,考虑下各种 DOM 操作,无疑增加很大的复杂性,而且设计之初只是为了完成简单的任务,并没有考虑如此复杂!
说起同步任务和异步任务,首先我们说说它们为什么会出现,由于 JS 是单线程的,我们只能等一个任务结束之后再去执行另外一个任务,这样如果遇到数据请求、文件操作需要等待很长时间, 为了提交效率,我们可以在等待的过程中去干其他的,这样异步任务就出现了,异步任务需要等待到结果之后才能接着去执行,而同步是立即执行的
同步任务
在主线程上排队执行的任务,只有前一个任务完成,下一个才可以执行
异步任务
不进入主线程,而进入任务队列,只有任务对列通知主线程的时候才会去执行
事件循环
注意
执行栈放的都是同步任务,任务队列是异步任务
是到时候(异步任务有了运行结果,在任务队列中放置一个事件(将其回调包装为一个任务,加入队列等待执行))之后才放入队列,不是到时间之后去执行。
netTick 同步任务执行之后,异步任务执行之前执行 setImmediate 每一轮的事件循环结束之后执行
宏任务和微任务都是属于任务对列之中的 宏任务:setTimeout、setImmediate、requestAnimationFrame 微任务:Promise.then()、MutationObserver
当然最后的顺序也很重要
再看一个
输出的结果为 a c d b
所有同步任务在主线程上执行,形成一个执行栈
主线程之外,还有一个任务队列,只要异步任务有了结果,就会将在任务队列中放置一个事件
一旦执行栈中的同步任务执行完毕,系统就会读取任务队列,对应的异步任务,结束等待状态,进入执行栈执行
主线程不断执行第三步
同一个类型的任务在同一个队列,不同类型的任务可以分属不同的队列,在一次事件循环中,浏览器可以根据的自己的实际情况从 不同队列中取出任务处理
浏览器必须有一个微队列,优先于其他任务执行
目前Google的实现
渲染主线程(执行全局JS)
延时队列
交互队列(优先级高于延时)
微队列(Promise.resolve().then(fun))
事件循环又叫消息循环,是浏览器渲染进程的渲染主线程的工作方式
在Chrome的源码中,会开启一个不会结束的for循环,每次循环从消息对垒的取出第一个任务执行,而其他进程只需要在合适的时候将任务加入队列的末尾即可。 过去将消息队列简单分为宏任务和微任务队列,现在已经无法满足复杂的浏览器环境,取而代之的是这一种更加灵活多变的处理方式
根据W3C的解释,每个任务有不同的类型,同类型的任务必须放在同一个队列,不同的任务可以属于不同的队列,不同任务队列有不同的优先级,在一个时间循环中, 由浏览器自行决定取哪一个队列的任务,但是浏览器必须有一个微队列,且微队列的具有最高的优先级
当 nodejs 启动时候,会初始化一个事件循环,执行代码脚本,脚本会进行 API 的调用,process.nextTick 会开始事件循环,zhu yao 主要分为以下 6 个阶段,这 6 个阶段执行完之后才算一次事件循环
requestAnimationFrame ,高级版的 setInterval,(可以这样去理解),主要目的下次渲染之前执行回调函数 requestIdlecallback 浏览器空闲状态执行,为了防止一直繁忙,浏览器设置了一个 timeout 参数
就是在每一次 Eventloop 的末尾,判断当前页面是否处于渲染时机,就是重新渲染。而这个所谓的渲染时机是这样定义的:比如 60Hz,即每秒刷新 60 次
独立于事件循环之外,他有自己的一个队列,当每个阶段完成后,如果存在该队列就会清空队列中的回调,并优先于微任务之前执行
浏览器和 node 中的事件循环的区别是,浏览器每次执行的是宏任务队列中的一个任务,执行完了之后再执行微任务,而 node 中的微任务是各个阶段都执行的