JavaScript 为什么需要事件循环
1,为什么JavaScript是单线程
javascrip是一个单线程的脚本语言,也就是说,同一个时间只能做一件事,JavaScript的单线程,与它的用途有关。它的主要用途就是与用户互动,以及操作dom。这决定了它只能是单线程,否则会带来很多问。假如,JavaScript同时有两个线程,一个线程在某个DOM上添加节点,一个线程删除了这个节点,最终浏览器以哪个线程为准。所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。 目前HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
2,单线程产生的问题
那么单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。如果前一个任务是异步(ajax,IO设备)的就需要一直等待,这时就会出现页面假死的状态。
JavaScript语言的设计者意识到这个问题,主线程完全可以不管IO设备,异步操作挂起等待中的任务,在运行在后面的同步任务,等待异步有了返回后在回头执行这个挂起的操作。
于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
3,任务队列(2022.10.27)
JS主线程顺序读取代码,形成一个执行栈(execution context stack) ; 碰到耗时的操作,也就是需要异步执行的代码,主线程就根据异步类型分派给其他的异步线程去处理这些操作,当他们处理完成后,将结果扔给任务队列; 当主线程把所有执行栈中的内容执行完成后,开始循环读取任务队列中,有完成的,就把这些异步的回调(callback)拉到主线程继续执行,如此反复,称为事件循环(Event Loop)
"任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。 除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生或完成时就会进入"任务队列",等待主线程读取。
回调函数\color{red}{回调函数}回调函数,就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。并且整个代码程序是有事件驱动的(点击事件、页面滚动、请求事件、定时器事件等)。
"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。如果只是一个队列的话遇到紧急的任务就需要等待所有任务队列执行完毕后,才能执行。所以任务队列中是双轨车道。一条放宏任务队列,一条放微任务队列。并且,微任务比宏任务的优先级高。
微任务在每一个宏任务中定义一个微任务队列,当该宏任务执行完成,会检查其中的微任务队列,如果为空则直接执行下一个宏任务,如果不为空,则依次执行微任务,执行完成才去执行下一个宏任务
4,事件循环(Event Loop)
- 1.进入到script标签,就进入到了第一次事件循环.
- 2.遇到同步代码,立即执行
- 3.遇到宏任务,放入到宏任务队列里.
- 4.遇到微任务,放入到微任务队列里.
- 5.执行完所有同步代码
- 6.执行微任务代码
- 7.微任务代码执行完毕,本次队列清空
- 寻找下一个宏任务,重复步骤1
总结
- js执行遇到wapApi,异步模块,放到异步模块中处理
- 执行同步代码,异步处理检查(满足条件放入任务队列)
- 同步执行完成,检查任务队列并执行
以上就是我对JS执行原理的一些整理和理解,希望能给读者带来一些帮助。如果有理解错误或表述不当的地方,请指正。
参考文献:
转载自:https://juejin.cn/post/7158674455407886349