1⃣️ 面向的读者
了解事件循环的整体过程,但是感觉一些细节处依旧模糊不清(没错就是之前的我了),因此整理了一下目前我对于事件循环的关键理解点与整体认知,希望能够起到帮助也欢迎探讨!
2⃣️ 理解关键点
1.宏任务与微任务与任务队列
误解:任务队列里面存放的是宏任务与微任务
正解:任务分为宏任务和微任务,但是宏任务和微任务的队列存放的是对应任务的回调函数
🌰1:宏任务比如setTimeout,那么他的回调函数 ,也就是第一个参数会在setTimeout执行完成之后被压入任务队列中的宏任务队列
🌰2:微任务比如Promise,那么他的回调函数 如then,catch等函数,会在Promise执行完成后被压入微任务队列
2.回调函数进入队列的时机
误解:遇到微任务宏任务后就将其回调函数压入队列
正解:对应任务的回调函数只有在该任务【执行完成之后】(不同任务执行完成的含义不同)才会压入对应队列,等待主线程的调用执行
🌰1:Promise的then和catch的回调函数,只有在Promise执行完成 即Promise的状态发生改变后,才会被压入微任务队列;
🌰2: 如setTimeOut的回调函数,只有在其执行完成 即等待指定时间后才会被压入宏任务队列
这也就能解释setTimeOut的第二个参数为什么不是实际延迟时间而是最小延迟时间,因为这个时间只决定了其回调进入宏任务队列的时间,而回调函数的执行需要等待前面的宏任务完全清空才能进行
3⃣️ 事件循环过程概述
1.背景概述:
JS在单线程的基础上需要实现异步任务的处理,需要在执行当前执行栈的脚本时将遇到的异步任务挂起,这些任务就包括宏任务与微任务,而宏任务与微任务的执行方式/顺序就被称作是事件循环。
2.具体过程:
- 在执行栈任务清空后,JS会检查任务队列中是否有宏任务的回调函数或者微任务的回调函数
- 如果微任务队列不为空(即存在微任务的回调函数)JS会清空微任务队列
- 清空后则判断宏任务队列中是否有对应的回调,如果有的话则执行下一个宏任务
- 这样一个宏任务与微任务交替执行 的过程即为事件循环的主体过程
3.细节过程
- 宏任务和微任务的回调函数在执行过程中是也可能碰到异步事件的 ,即其可能也会产生出新的宏任务和微任务,并且在执行完成之后会将对应的回调函数压入对应的队列。
- 因此具体过程中第二步的"清空微任务队列"是得要保证完全清空,即当前执行栈为空时,微任务队列也得为空,才会去执行下一个宏任务
4.问题思考
问题描述:我在执行微任务队列的最后一个微任务的回调函数时,遇到了一个新的微任务A,那么当我把当前回调函数执行完成后,JS应该怎么做?
理解关键点:微任务回调函数压入队列的时机
解答 :这种情况应当分类讨论,判断是否执行宏任务的唯一条件还是:执行栈为空且微任务队列为空
- 如果当前回调函数执行完之后,新的微任务A还没执行完成,那么现在就满足"执行栈为空且微任务队列为空"的条件,即执行下一个宏任务,微任务A等待执行完成后再将回调函数压入微任务队列
- 如果当前回调函数执行完之后,新的微任务A已经执行完成并且回调函数已经压入微任务队列,那么微任务队列不为空,则继续执行下一个微任务,直到其为空为止
4⃣️ 总结
事件循环的过程即宏任务与微任务交替执行的过程,不过在执行过程中,其是按照一定规律执行:先清空微任务队列,再执行下一个宏任务队列中的回调;宏任务与微任务的回调函数在其执行完成之后进入队列;执行过程中遇到的宏任务与微任务在其执行完成之后会将对应的回调函数加入对应队列。以此"循环"执行 故称事件循环
面试问到事件循环是什么应该怎么答?背景+过程:
- JS在单线程的基础上需要实现异步任务的处理,需要在执行当前执行栈的脚本时将遇到的异步任务挂起交给其他的线程执行,这些任务就包括宏任务与微任务,而宏任务与微任务的执行顺序就被称作是事件循环。
- 其具体循环方式为宏任务与微任务交替执行,即执行栈为空时,检查微任务队列,若其不为空则依次执行使之为空;执行栈与微任务队列皆空时,执行下一个宏任务的回调函数
拓展思考:JS执行时页面能够正常渲染更新吗,如果不能的话页面的渲染是在什么时候进行的呢,好吧我也不是很清楚,希望得到解答🙏🙏🙏🙏🙏
感谢阅读,希望有所帮助🙏🙏🙏🙏🙏