JavaScript中的事件循环机制

提示:微任务和宏任务是一种过时的说法了,据我所了解,已经没有宏任务的说法了

Js为什么是单线程的?

如果有三个按钮,其回调函数里面处理的代码量用的时间各不相同,用户依次快速的点击三个按钮,此时用户希望的是,先点击的先执行,后点击的后执行,可是由于是Js多线程的,每一个回调函数都会被注册成一个线程,这时,肯定是用时少的先执行。这就造成了一个很大的问题,用户体验很不好

可能有人会问,定时器里的代码为什没有按照顺序执行呢?

答:JavaScript采用了一种排队机制,将异步代码放入了消息队列中,待同步代码执行完后,如果消息队列中存在异步代码需要执行,就会唤醒Js主线程去执行这些异步代码

队列里的代码,谁先放进去,谁先执行。

Event Loop 图解

单线程也是存在着异步和同步的问题。这也正是我们今天的重点内容,事件循环机制(event loop)

先来一张图,感受一下

在这里插入图片描述
代码执行时,肯定是先执行同步代码,再执行异步代码,同步代码执行完后,其代码里定时器的回调函数,Dom事件的回调函数,Ajax请求的回调函数等这些异步代码,会由浏览器WebAPIs管理模块里的对应模块相应管理。当这些回调函数,到达要执行的条件(比如定时器到时间了,用户点击了),会由相应的模块送到队列里,然后执行。

先来看一段代码

setTimeout(() => {
	console.log('3')
},0)

Promise.resolve(2).then(
	value => console.log(value)
)

console.log('1')

控制台打印 1 2 3

你可能会很奇怪,定时器的第二个参数是0,即立马放进了队列里,Promise也是立马达到了满足条件。也会放进队列里,又根据代码的先后执行顺序,队列里肯定先放定时器的回调函数,再放Promise的回调函数,等同步代码执行完后,异步代码就是先执行定时器里回调函数,再执行Promise里的回调函数。控制台输出应该是1 3 2 才对啊!!!可是为啥是 1 2 3 呢?

这就引出了我们今天所学的第二个重点内容?宏任务与微任务

宏任务与微任务

其实,我们的上图是没有画完整的,下面来一张完整图,大家来感受一下

在这里插入图片描述

我们可以看出,队列里又进行了划分,又分为宏队列微队列

宏队列里放的是宏任务

微队列里放的是微任务

(注意:上图的标注的分线程是浏览器的,不是我们的Js代码的。我们只是写的代码交给了浏览器管理)

其Promise和Mutation(vue里会遇到mutation)里的回调函数,会放进微对列里

微队列优先级是高于宏队列优先级,所以上述代码,输出 1 2 3就有理可寻啦

我们通过几个小题练习一下

setTimeout(() => {
	console.log('4')
},0)

setTimeout(() => {
	console.log('5')
},0)

Promise.resolve(2).then(
	value => console.log(value)
)

Promise.resolve(3).then(
	value => console.log(value)
)

console.log('1')

//控制打印: 1 2 3 4 5
setTimeout(() => {
	console.log('4')
},0)

Promise.resolve(2).then(
	value => console.log(value)
)

Promise.resolve(3).then(
	value => console.log(value)
)

console.log('1')

//控制打印: 1 2 3 4

学到这里,你可能觉得已经结束了,其实并不,还有一个很重要的知识点没讲呢,我们接着学习

setTimeout(() => {
	console.log('4')
},0)

setTimeout(() => {
	console.log('5')
},0)

Promise.resolve(2).then(
	value => console.log(value)
)

Promise.resolve(3).then(
	value => console.log(value)
)

console.log('1')

//控制打印: 1 2 3 4 5
setTimeout(() => {
	console.log('4')
	Promise.resolve(3).then(
		value => console.log(value)
	)
},0)

setTimeout(() => {
	console.log('5')
},0)

Promise.resolve(2).then(
	value => console.log(value)
)

console.log('1')

控制台打印1 2 4 3 5

别急,这里先给出结论:每执行一个宏任务时,都会检查微队列中是否有待执行的的回调,优先执行微任务

所以打印4后,将输出3的回调放进了微队列中,宏队列在执行输出5的回调函数前,先把微队列给清空了

  • 31
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值