js事件循环机制-宏任务微任务

34 篇文章 2 订阅

一,js是基于V8引擎的单线程运行机制:也就是在主线程中,代码的执行是顺序执行的

但是如果遇到了一些异步操作如:

宏任务:setTimeout、setInterval, Ajax,DOM事件
微任务:Promise async/await

它们的执行需要一定的时间,如果还是单单使用主线程,就会因为这些耗时长的操作而堵住,程序无法接着执行,这就是平时所说的阻塞。
为了达到非阻塞的目的,就有了异步非阻塞的概念,在js中,异步操作的处理都是异步非阻塞。
举一个简单的例子:

烧开水:
你有一堆事abc情要做,还要烧一壶开水,但是烧开水需要一定的时间,为了把等待的这个时间利用起来,而不是你守在热水壶旁边啥也干不了等水烧开。(同步阻塞)

人们在热水壶上安装一个哨子,当水烧开就会发出声音,提醒你水烧开了。这烧水的期间,你就可以去做其他事情,只要听到哨声,把电关了就行。(异步非阻塞)

照着这个思路,于是在主线程的执行栈之外,又有名为事件队列的一个队列,里面就存放着异步操作的结果(水壶哨声响了的事件,需要你去关电)。
接下来,先不讲宏任务微任务,先讲异步操作和同步操作的处理

二,同步操作和异步操作的处理

在这里插入图片描述
这张图是其他博客上复制来的哈,已经很明确地表现出同步操作和异步操作地处理方式了。
也就是说,主线程中的所有代码依次顺序执行,遇到同步则直接执行,遇到异步,则在Event Table中注册异步(类比于烧水事件中的把水壶的电插上)。这些操作无需花费多少时间的。
等到异步操作的结果出来了,就把回调函数注册到事件队列中去(类比于水壶的哨声响了,需要人去关电这一事件)。
另外js的事件执行有一个轮询的机制:等到主线程空了,就去事件队列中查找要执行的事情放入主线程,然后同步的执行,异步的注册,而注册的异步结果出来了放入事件队列中。主线程空了再去,空了再去……(这就是Event Loop事件循环)
所以说下面这个例子:

		<script type="text/javascript">
		    setTimeout(function(){
				console.log('定时器第一个')
		    },100);
			setTimeout(function(){
				console.log('定时器第二个')
			},0);
		    console.log('代码执行结束')
		</script>

返回的结果就会是:
在这里插入图片描述
在这里插入图片描述

三,异步操作又区分为宏任务和微任务

宏任务

包括 整体代码script,setTimeout,setInterval ,setImmediate,I/OUI renderingnew ,Promise*

DOM渲染后触发

微任务

包括 Promises.(then catch finally),process.nextTick, MutationObserver
DOM渲染前触发

区别

宏任务和微任务的区别在于在事件循环机制中,执行的机制不同
每次执行完所有的同步任务后,会在任务队列中取出异步任务,先将所有微任务执行完成后,才会执行宏任务
所以可以得出结论, 微任务会在宏任务之前执行。
我们在工作常用到的宏任务是 setTimeout,而微任务是 Promise.then
注意这里是Promise.then,也就是说 new Promise在实例化的过程中所执行的代码是同步的,而在 then中注册的回调函数才是异步。

也就是说,遇到异步函操作,还需要判断是宏任务还是微任务,宏任务的话,就把异步操作的结果加入宏任务队列,微任务的话,就加入到微任务队列。
于是,异步到的队列,就由原来的一个事件队列,变成了宏队列和微队列两个,而主线程空了的话,会先去微队列中查找(若在这个过程中,微队列的事件又产生的新的微任务加入队尾,也会在本次循环中进行处理,简而言是就是把每轮循环把微队列搞空),然后再去宏队列中查找(同样的,把宏队列搞空)。

setTimeout(function(){
    console.log('1')
});
new Promise(function(resolve){
    console.log('2');
    resolve();
}).then(function(){
    console.log('3')
});
console.log('4');
new Promise(function(resolve){
    console.log('5');
    resolve();
}).then(function(){
    console.log('6')
});
setTimeout(function(){
    console.log('7')
});
function bar(){
    console.log('8')
    foo()
}
function foo(){
    console.log('9')
}
console.log('10')
bar()

解析:

首先浏览器执行Js代码由上至下顺序,遇到setTimeout,把setTimeout分发到宏任务Event Queue中
new Promise属于主线程任务直接执行打印2
Promis下的then方法属于微任务,把then分到微任务 Event Queue中
console.log(4)属于主线程任务,直接执行打印4
又遇到new Promise也是直接执行打印5,Promise 下到then分发到微任务Event Queue中
又遇到setTimouse也是直接分发到宏任务Event Queue中,等待执行
console.log(10)属于主线程任务直接执行
遇到bar()函数调用,执行构造函数内到代码,打印8,在bar函数中调用foo函数,执行foo函数到中代码,打印9
主线程中任务执行完后,就要执行分发到微任务Event Queue中代码,实行先进先出,所以依次打印36
微任务Event Queue中代码执行完,就执行宏任务Event Queue中代码,也是先进先出,依次打印17。
最终结果:24510893617
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值