事件循环

        事件循环不仅包括事件队列,还包括任务,任务分为两类:一是宏任务,二是微任务。宏任务主要包括 创建主文档对象、解析HTML、执行主线(或者全局)Javascript 代码、更改当前URL 以及各种事件如页面加载、输入、网络事件和定时器事件等。从浏览器的角度看,宏任务代表一个个离散的、独立工作单元。运行完任务后,浏览器可以继续其他调度,比如重新渲染页面的UI或者执行垃圾回收。

        微任务是更小的任务。微任务更新应用程序的状态,但是必须在浏览器任务继续执行其他任务之前执行,浏览器任务包括重新渲染页面的UI 。微任务的例子有 promise 回调函数、DOM发生变化等。 微任务要求尽可能快、通过异步方式执行,同时不能产生新的微任务。微任务使得我们能在重新渲染UI之前执行指定的行为,避免不必要的ui重绘,因为UI重绘会使应用程序的状态不连续。

       JavaScript 的调用栈: js 调用栈采用的是后进先出的规则,当函数执行的时候,就会被添加到执行栈的顶部,当执行栈执行完成后,就会从栈顶移出,直到栈内被清空。

      JavaScript 同步任务和异步任务: JavaScript是单线程的,而单线程的任务被分为同步任务和异步任务,同步任务会在调用栈中按照顺序等待主线程任务依次执行,异步任务会在异步任务有了结果后将注册的回调函数放入任务队列中等待主线程空闲或者调用栈被清空的时候,被读取到栈内等待主线程的执行。而任务队列是队列的一种,是一种先进先出的数据结构。

      事件循环基于两个基本原则:一次处理一个任务、一个任务开始后直到运行完成,不会被其他任务中断。

      事件循环的基本过程(简化版): 在一次迭代中,事件循环首先检查宏任务队列,如果宏任务等待,则开始执行宏任务,直到该任务运行完成或者队列为空,这时事件循环将去处理微任务队列;如果有任务在队列中等待,则事件循环将依次开始执行,直到队列中所有微任务执行完毕。单次循环迭代中,最多处理一个宏任务,其余的在队列中等待;而队列中所有的微任务都会被处理。

     当微任务队列处理完成并清空时,事件循环会检查是否需要更新UI渲染,如果是就会更新视图,当前事件循环结束,之后再次检查宏任务队列,开启新一轮的事件循环。所有微任务会在下一次渲染之前执行完成。微任务和宏任务两类任务队列都是独立于事件循环的,这意味着任务队列的添加行为和检测行为也发生在事件循环之外。

    如果微任务队列中含有微任务,不论队列中等待的其他任务,微任务都将获得优先执行权。

   

以上例子中,在第一个按钮的单击处理器执行过程中,创建一个已经兑现的promise, 在微任务对列中,出现一个在等待中的微任务,该微任务会尽可能地被执行,但是不会中断当前正在运行中的任务。所以当firstButton 单击事件执行完成后,按照先后顺序,应该执行secondButton 才算公平,但是微任务具有优先执行权,所以当当firstButton 单击事件执行完成后,会立即执行promise 对象的回调函数,而更早在队列中等待的secondButton 单击任务则继续等待。

     当且仅当两个宏任务之间没有微任务时,可以在两个宏任务之间重新渲染页面,比如可以在执行主线程和第一个按钮单击任务之间;但是在第一个按钮单击任务处理完成之后无法立即重新渲染页面,而是需要优先处理promise 。在微任务处理完成之后,只有微任务队列中没有正在等待的微任务,才可以重新渲染页面。需要注意的是:无法停止微任务的运行,无法在微任务之前添加其他微任务,所有微任务的优先权高于secondButton 单击任务。只有当微任务队列为空时,事件循环才会重新渲染页面,继续执行secondButton 单击任务。

 

一道面试题镇楼:

换作以前,哐哐写下1,2,4,3  

但是今天了解了事件循环,就要小心分析:从任务队列开始,首先会打印 1 ,这是毫无疑问的; 后来遇到宏任务  第一个setTimeout 交给异步处理模块,同理遇到第二个setTimeout, 同样交给异步处理模块; 然后继续执行,打印 数字 4;

至此,同步任务执行完毕,开始读取宏任务队列中的任务,先读取第一个setTimeout  的回调函数, 定时器的等待时间为0 s ,所以会立即打印 2 , 但是在w3c 的HTML标准中规定,setTimeout 中低于 4ms 的时间间隔算为四毫秒。这不影响先打印2 ;

接着读取 第二个 setTimeout  宏任务 ,等待两秒打印3 ,所以最后的打印结果为  1 ,4 ,2, 3

改一下题目:

结果还是 1,4,3,2  (原因上面说了)

 

日常自问: 为什么添加任务到任务队列中必须在任务队列外?

              答:如果添加任务到队列是事件循环的一部分,那么当JavaScript代码执行时发生的事件都将被忽略,体验极差,问题极大。

又问每一次的事件循环的迭代不应超过 16毫秒?

            答: 为了实现应用程序平稳运行,浏览器试图每秒大约渲染60次(60fps) 。因为渲染在事件循环结束时执行,每个迭代就不应该超过16毫秒的或者持续更长的时间,那样会导致应用卡顿、缓慢,进而导致用户体验极差。60fps 通常时检验体验是否流畅平滑的标准(16ms内渲染一帧)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值