基于JakeArchibald分享的事件循环

https://www.youtube.com/watch?v=cCOL7MC4Pl0&t=1592s原视频链接

        在网上很多关于eventloop的文章都在描述javascript的eventloop,我经过查看一些大佬的分享加上自己的理解,觉得javascript本身是与eventloop是没有任何关系的,javascript本身就是一个单线程的语言,让事情变得复杂的是javascript的运行时(runtime)。

        javascript的运行需要宿主环境,通常是在浏览器内,在浏览器的运行过程中,可能伴随着事件的交互,http的请求,以及页面的渲染,所以浏览器需要对每个线程做出合理的处理方式。

        事件循环会在各个线程之间来回“跑”,当某一个线程中有任务时,就会去执行,比如当用户点击了某个按钮,事件循环就回去执行点击的任务,当屏幕需要刷新的时候,就会去进行css计算及界面的渲染。 

         假设页面上有一段文字及一张gif,当用户点击了按钮后,事件环应该会在浏览器事件触发线程中执行这个无限循环,导致另外的交互行为将无法被执行比如选取一段文字,同时也会阻止浏览器的更新渲染,gif也将停止。

        实际上在比较新的Chrome中,交互的事件被阻止了,但是gif还会正常的展示,在Safari或是Firefox中gif都会停止,难道是Chrome的渲染线程做了特殊的处理么?

        答案当然是否定的,在清除具体原因之前还需要知道一件事情,raf(requestAnimationFrame)是会发生在css计算及界面渲染之前的。

        

         在之前我们可能会写出这样的代码,希望一个盒子先向右移动500px,然后添加一个过渡效果后慢慢回到原来的位置,但实际上根本没有任何效果。

        因为当点击了按钮后,事件环去开始执行了点击事件内的代码,当执行完之后,无论之前做了什么操作,进入渲染阶段的时候,浏览器只会看最后的结果。

        所以我们要加上requestAnimationFrame来保证代码在下一帧中才会执行,于是

         这种代码自然的写出来了,然后满怀信心的去点了按钮,结果,还是没有任何效果。(这个地方视频中有问题)

        这就是之前说的raf是发生在css计算及界面渲染之前的,点击按钮后

        首先事件环开始执行click事件,发现requestAnimationFrame,于是先将内部的代码“放在一边”,click事件处理完,事件环开始继续“转”,某一时刻,屏幕该刷新,事件环进入了UI渲染的线程,在线程内部会先进行raf,所以盒子translateX又回到了最初的起点,然后开始css计算及界面渲染,最后看到的效果就是没有任何变化。

        想到这里很自然的就可以想到那我们再加一层requestAnimationFrame,让他再下一帧再改变不就行了么。

        还是不行!可以发现,每次点击的时候盒子发生了明显的抖动,问题就处在了

        boxs[0].style.transition = 'transform 1s ease'

        这行代码,盒子从原来的位置移动到500px的时候需要1秒钟!而一秒钟有60帧(取决于屏幕刷新率),所以盒子会有一帧的移动动作,但是下一帧马上回到了原来的位置。

        

         这样才能看到一个盒子从500px处移回原位置,如果想过渡的效果放在外面,可以试试套61层requestAnimationFrame,你就能看到一个盒子从原来的位置经过1s移动到500px位置,再经过1s回到原来的位置,如果你愿意套61层的话。

        回到之前的问题,在较新版的Chrome中为什么事件环在执行while(true)时gif还在更新?

这个问题我查阅了v8近两年发布的 更新内容还没找到答案,以后如果找到了答案会及时更新在这里,如果有知道的小伙伴也可以分享一下原因。虽然没找到答案,但是一个简单的测验可以证明的确阻止了渲染线程。

        

        另外,如果在点击按钮时调用setTimeout无限循环及微任务的无限循环会发生什么

         setTimeout的无限循环不会影响任何的交互事件及渲染,而微任务与while(true)相同。

        这里提到了三个队列,微任务队列,raf回调队列,任务队列,事件环在处理他们的时候各不相同,处理任务队列时,每次只处理一个任务,然后”跑去“处理其他的线程等,raf回调队列,每次处理完当前所有的任务,如果之间又套着raf,这些任务会在下一帧被处理,微任务队列会一直被处理直到队列为空,所以如果添加微任务的速度比处理的速度快,事件环就会永远的“留在”微任务队列,等同于while(true);

        网上大多数事件循环的文章都在讲先执行什么再执行什么,这些东西我也能张口就说出来,网上出的五花八门的题也基本没出过错,但是始终感觉对事件循环很模糊,个人觉得这样没办法真正的理解事件循环,偶然间看到了这个视频,感觉豁然开朗,经过了一些自己的测试,对一些视频内稍有问题的地方进行了总结,视频中还分享了很多知识,感兴趣的可以看原视频。

        本文章有任何不正确的地方,欢迎指正,本人一定虚心改正,提升自己的技术。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值