面试的时候经常会被问到js的事件循环,有时还会看到event loop或者事件轮询之类的,说的都是同一个东西。
总所周知呐,这JavaScript是个单线程,就好像是银行处理业务,只有一个窗口在运行,只能排队,即使你排成多列(多线程),也不能增加处理速度,反而影响到其他业务。
所有程序都在一个主线程上运行,时间累加,不能插队,这种模式称为 同步模式 或者 阻塞模式,也就意味着程序处理时间过长会导致假死,报错会导致程序中断执行。
那怎么办呢?有没得其他方案呢?
于是上帝就在程序中弄了两个线程,一个负责程序本身的运行,叫做"主线程";另一个负责异步任务的,被称为 “Event Loop线程 ”。
两个线程和多线程不就是同一个意思吗?NO! NO! NO!
它其实还是只有一个线程在处理,我们需要把任务进行分类,调整任务的执行先后顺序
1.在执行主线程的任务时,如果有异步任务,会进入到Event Table并注册回调函数,当指定的事情完成后,会将这个回调函数放到 callback queue 中
2.在主线程执行完毕之后,会去读取 callback queue中的回调函数,进入主线程执行
3.不断的重复这个过程,也就是常说的Event Loop(事件循环)了
这样提高了整个运行效率,这种运行方式称为“异步模式”或“非堵塞模式”。
同步任务一口气执行完,那异步任务是不是一口气执行完呢?
答案是NO!
异步任务又分为宏任务(macrotask)跟微任务(microtask),他们之间的区别主要是执行顺序的不同。
宏任务(macrotask queue):setTimeout,setInterval,setImmediate(node独有),requestAnimationFrame(浏览器独有),I/O(输入/输出),UI rendering (浏览器独有)
微任务(microtask queue):process.nextTick (Node独有),Promise,Object.observe,MutationObserver
两种任务分别进入宏队列(macrotask queue)和 微队列(microtask queue)
执行性顺序:
将全局同步代码放入调用栈,遇到异步任务,分别放入宏队列或微队列。
调用栈中同步代码执行完后,会将微队列中的微任务放入调用栈执行,期间产生的新微任务会放到微队列末尾,在当前周期执行
当前微队列执行完毕后,会一次一次取宏队列中的宏任务到调用栈执行,如果又产生微任务,微任务会放在微队列中,每执行一个宏任务都会检查微队列,并优先清空微队列,直至所有任务全部清空
好了,上面的看懂了吗?有点晕吗?
举个栗子:
还是在那个孤零零的银行窗口,排队中,有人是普通号码(宏任务),有人是vip号码(微任务),等vip全部处理完了,就开始处理普通号码,处理普通号码过程中,又来了两个新的vip(微任务),原本要排到号的又要被这两个vip插队,所有vip处理完了又开始重新喊普通号。
微任务就是拽!
看懂了就来做个题,注意:Promise的函数参数是同步任务哦,只有状态落定程序resole().then() 是异步。
console.log(1)
setTimeout(() => {
console.log(2)
},0)
new Promise((resove,reject) => {
console.log(3)
resove()
}).then(res => {
console.log(4)
})
console.log(5)
setTimeout(() => {
console.log(6)
new Promise((resove,reject) => {
console.log(7)
resove()
}).then(res => {
console.log(8)
})
},0)
setTimeout(() => {
console.log(9)
},0)
console.log(10)
答案在下面:
防止偷瞄 1 2 3 4 5 6 7 8 9 10
正确答案:
000000000000000000000000000 1 3 5 10 4 2 6 7 8 9 00000000000000000000000000
另外:
宏任务和微任务到底是谁最先执行呢?
实际上,通常认为所有的同步任务为一个宏任务,所以宏任务是第一个执行的。