为什么有事件循环?
js单线程,多任务会排队,等待上一个任务执行完毕,如果某个任务执行时间很长,会造成阻塞,因此,JS引入了事件循环机制
事件循环是啥
JS单线程任务分为:同步任务、异步任务
- 同步任务:在主线程中排队执行,前一个任务执行完毕,再执行下一个任务
- 异步任务:不进入主线程,异步任务有结果后,把回调函数放到任务队列上,等主线程空闲时执行
主线程不断在任务队列中读取事件,叫做事件循环
Node中的事件循环
Node基于JavaScript,运行在单个线程中,也就是说,是同步的。然而,输入输出(I/O)和其他原生API是异步运行的,有单独的线程。Node通过事件循环管理这种时序中断。
假如你到熟食店买午餐,排队下单,拿到一个订单号。你坐下来等待,读读报纸、刷刷微博。与此同时,你的订单进入另一个队列,等待熟食店员工处理。但是,午餐并不一定总是按照下单的顺序完成,有些订单可能需要烘烤,耗时长一些。熟食店员工接到订单,把准备好的食材放入烤箱,按下计时器便去做其他任务了。
计时器铃响后,熟食店员工迅速放下手中的任务,从烤箱中取出你的午餐。你听到叫号后,起身取走午餐。如果同时处理多个耗时的午餐食材,熟食店员工会为每个食材按下一个计时器,按照计时器响铃的顺序依次处理
所有Node进程都安装熟食店处理订单的模式排队:先收到的订单先发给熟食店员工(线程)。然而,某些操作,例如I/O,就像需要额外花时间烘烤的午餐订单一样,不要求熟食店员工停下手中的工作等待烘烤结束。烘烤计时器就像Node事件循序中的消息,对请求的操作触发最后的活动
异步任务中的两种任务
宏任务(macroTask || Task):script(外层同步代码)、setInterval、SetTimeout、setImmediate(IE特有)、I/O
微任务(microTask || Job): 当前task执行结束后立即执行的任务。Promise.then/catch/finally、MutationObserver回调(H5特性,用于监听节点变化)
单个宏任务执行完后,检查微任务队列是否为空,如果不为空,按先入先出,全部执行完微任务后,在执行下一个宏任务
console.log(1);
// 异步,不能设置延迟时间,只能执行一次
setImmediate(function () {
console.log('2');
});
console.log(3);
// 1 -> 3 -> 2
为什么要有微任务?
事件队列是先进先出的结构,排在前面的优先被主线程读取,如果来了个高优先级的任务,让它去排队就不够人性化了
- 你去银行柜台办业务,银行的人会问你办什么业务,根据业务不同可能需要填表,然后让你去取号
- 你要取10万块,这是不需要填表的
- 柜员办理业务相当于主线程
- 你先去取号排队,等待叫号
- 轮到你时,发现银行卡不见了,取钱的业务办不了 (宏任务)
- 你需要填一份表,然后办理挂失业务,拿到新卡后,才能继续执行取钱任务 (微任务)
如果没有微任务,柜员叫你去填表然后重新排队(显然不合理)
事件循环练习题
async function async1() {
console.log("async1 start")
await async2()
console.log("async1 end")
}
async function async2(){
console.log("async2")
}
console.log("script start")
setTimeout(function(){
console.log("setTimeout")
}, 0)
async1()
new Promise(function(resolve){
console.log("promise1")
resolve()
}).then(function(){
console.log("promise2")
})
console.log("script end")
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
console.log("start");
setTimeout(() => {
console.log("children2")
Promise.resolve().then(() =>{
console.log("children3")
})
}, 0)
new Promise(function(resolve, reject){
console.log("children4")
setTimeout(function(){
console.log("children5")
resolve("children6")
}, 0)
}).then(res =>{
console.log("children7")
setTimeout(() =>{
console.log(res)
}, 0)
})
start
children4
children2
children3
children5
children7
children6