EventLoop
Contents
-
概念:
Event Loop
即事件循环,是指浏览器或Node
的一种解决javaScript
单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。- 调用栈(Call Stack)
- 在
JavaScript
运行的时候,主线程会形成一个栈,这个栈主要是解释器用来最终函数执行流的一种机制。通常这个栈被称为调用栈Call Stack
,或者执行栈(Execution Context Stack
)。
- 在
- 任务队列(Task Queue)
JavaScript
将所有执行任务分为了同步任务和异步任务,异步任务分为宏任务与微任务- 同步任务的执行,其实就是跟前面那个案例一样,按照代码顺序和调用顺序,支持进入调用栈中并执行,执行结束后就移除调用栈。
- 而异步任务的执行,首先它依旧会进入调用栈中,然后发起调用,然后解释器会将其响应回调任务放入一个任务队列,紧接着调用栈会将这个任务移除。当主线程清空后,即所有同步任务结束后,解释器会读取任务队列,并依次将已完成的异步任务加入调用栈中并执行。
- 异步任务并不是直接进入任务队列
- 任务队列分为宏任务队列与微任务队列
- 调用栈(Call Stack)
-
浏览器端事件循环流程:
- 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务(整体代码
script
),放入调用栈,开始执行; - 执行完该宏任务下所有同步任务后,即调用栈清空后,该宏任务被推出宏任务队列,然后微任务队列开始按照入队顺序,依次执行其中的微任务,直至微任务队列清空为止;
- 当微任务队列清空后,一个事件循环结束;
- 接着从宏任务队列中,找到下一个执行的宏任务,开始第二个事件循环,直至宏任务队列清空为止。
- 从宏任务队列中,按照入队顺序,找到第一个执行的宏任务(整体代码
-
背景:JavaScript是一个单线程的脚本语言,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。为了解决排除等待问题,在
JavaScript
中,异步任务被分为两种,一种宏任务(MacroTask
)也叫Task
,一种叫微任务(MicroTask
)。 -
原理:
- 任务(
Task
)与微任务(MicroTask
)- 常见宏任务:
script
、setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
。 - 常见微任务:
MutationObserver
、Promise.then()或catch()
、Promise为基础开发的其它技术,比如fetch API
、V8
的垃圾回收过程、Node独有的process.nextTick
。
- 常见宏任务:
- 浏览器:
- 一开始整个脚本作为一个宏任务执行
- 执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
- 当前宏任务执行完出队,检查微任务列表,有则依次执行,直到全部执行完
- 执行浏览器UI线程的渲染工作
- 检查是否有
Web Worker
任务,有则执行 - 执行完本轮的宏任务,回到2,依此循环,直到宏任务和微任务队列都为空
- node:
- 任务(
微任务与宏任务
这个就像去银行办业务一样,因为柜员同时职能处理一个来办理业务的客户,这时每一个来办理业务的人就可以认为是银行柜员的一个宏任务来存在的,当柜员处理完当前客户的问题以后,选择接待下一位,广播报号,也就是下一个宏任务的开始。 所以多个宏任务合在一起就可以认为说有一个任务队列在这,里边是当前银行中所有排号的客户。 任务队列中的都是已经完成的异步操作,而不是说注册一个异步任务就会被放在这个任务队列中,就像在银行中排号,如果叫到你的时候你不在,那么你当前的号牌就作废了,柜员会选择直接跳过进行下一个客户的业务处理,等你回来以后还需要重新取号
而且一个宏任务在执行的过程中,是可以添加一些微任务的,就像在柜台办理业务,你前边的一位老大爷可能在存款,在存款这个业务办理完以后,柜员会问老大爷还有没有其他需要办理的业务,这时老大爷想了一下:“最近P2P爆雷有点儿多,是不是要选择稳一些的理财呢”,然后告诉柜员说,要办一些理财的业务,这时候柜员肯定不能告诉老大爷说:“您再上后边取个号去,重新排队”。 所以本来快轮到你来办理业务,会因为老大爷临时添加的“理财业务”而往后推。 也许老大爷在办完理财以后还想再办一个信用卡?或者 再买点儿纪念币? 无论是什么需求,只要是柜员能够帮她办理的,都会在处理你的业务之前来做这些事情,这些都可以认为是微任务。
- 一个进程中只有一个主线程
- 进程是CPU资源分配的最小单位,线程是CPU调度的最小单位
- 进程好比一个工厂,有单独专属自己的工厂资源(内存空间)
- 线程好比工厂中的工人,一个工厂由一个或多个工人组成,线程是一个进程中代码的不同执行路线
- 多个工厂之间相互独立
- 以Chrome浏览器中为例,当你打开一个 Tab 页时,其实就是创建了一个进程,一个进程中可以有多个线程(下文会详细介绍),比如渲染线程、JS 引擎线程、HTTP 请求线程等等。当你发起一个请求时,其实就是创建了一个线程,当请求结束后,该线程可能就会被销毁。
Author gsemir
LastMod 2022-07-21