事件循环与宏任务微任务

事件循环(Event loop)

事件循环即Event loop,是JavaScript的一种执行机制,为的是解决单线程的js在执行代码时不发生阻塞的一种机制。

在理解Event loop之前,需要先引入两个概念,即队列

两个不恰当的比喻,把比作一个瓶子,而队列则是一根的管子。

用这两种容器来装水,假设我们把水倒到容器里时,水并不会发生流动。那么杯子里的水要倒出来,则会发生先加入的水最后才被倒出来(先进后出),且不论是加水还是倒水都只能从瓶口操作。

反观管子,我们将水从一侧加入,水便会从另外一侧流出(先进先出),且我们只能从侧加水让水另一侧流出,没法让其从一侧加从一侧流出(因为我们的假设是水不会发生流动)。

对上述概念理解了之后,我们在来看栈和队列的定义,相信结合着这个两个例子,定义理解起来也会容易很多。

栈(stack)

在计算机科学中是限定仅在表尾进行插入删除操作的线性表。是一种数据结构,它按照先进后出的原则存储数据,先进入的数据被压入栈底后进入的数据栈顶,需要读数据的时候从栈顶开始弹出数据

是只能在某一端插入删除特殊线性表。如下图所示。

图1栈

队列(queue)

一样,队列是一种操作受限制的线性表

进行插入操作的端称为队尾,进行删除操作的端称为队头。 队列中没有元素时,称为空队列

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出。如下图所示。
图2 队列

同步任务和异步任务

js中将任务分为同步任务和异步任务,其中同步任务在执行栈中依照顺序依次执行。

而异步任务则交由浏览器去执行,当异步任务执行完以后,会被放入任务队列中,等执行栈中的任务执行完了以后,会将任务队列中的任务放到执行栈中执行。

执行栈中任务执行完,又会去轮询任务队列,这个过程不停循环进行,因此,也叫事件轮询或事件循环。

js的执行过程为:执行栈=>任务队列,任务队列中的任务都为异步任务执行后的回调,而异步任务又分为宏任务和微任务,两者的执行顺序是先执行微任务,然后再执行宏任务,即微任务=>宏任务。(也有一种说法是先执行宏任务再执行微任务,因为script代码也属于宏任务)如已了解宏任务与微任务可跳过以下内容至例子。如图3为事件循环的图示。

事件循环

宏任务与微任务

我们不难发现宏任务和微任务是同属于一个队列的,因此在执行时本轮循环中的微任务实际上是在插队,这样微任务中所做的状态修改,在下一轮事件循环中也能得到同步。

因此执行过程为:先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务

宏任务与微任务的种类

宏任务:script、setTimeout、setInterval、postMessage、MessageChannel、UI render(浏览器独有)、setImmediate(Node.js 环境独有)

微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)

目前我常遇到的就只有宏任务:script、setTimeout、setInterval和微任务:Promise.then,另外async和await一起时,await会将它之后的代码变为微任务。

例子

console.log('1、script1');

setTimeout(function() {
  console.log('5、宏任务');
}, 0);

Promise.resolve().then(function() {
  console.log('3、微任务1');
}).then(function() {
  console.log('4、微任务2');
});

console.log('2、script2');
///执行结果应为:
//1、script1
//2、script2
//3、微任务1
//4、微任务2
//5、宏任务

下面为执行过程的图解
例1图解
首先整个script被压入执行栈中,然后开始执行同步代码,即先执行console.log(‘1、script1’)再执行console.log(‘2、script2’)。
然后我们可以看到setTimeout为宏任务,promise.then为微任务,此处为两个.then,所以有两个微任务
在执行时,先执行微任务,后执行宏任务。两个微任务被推入任务队列中,任务队列遵循先进先出原则,所以执行顺序为:console.log(‘3、微任务1’)=>console.log(‘4、微任务2’),当微任务执行完以后,将宏任务推入任务队列中,随后将任务队列中的宏任务压入执行栈中执行console.log(‘5、宏任务’)。最后执行完宏任务,script也出栈,程序整体就执行完了。打印结果为:
1、script1
2、script2
3、微任务1
4、微任务2
5、宏任务

练习

///

console.log('1、Script')
setTimeout(() => {
  console.log('4、宏任务1')
  Promise.resolve().then(function() {
    console.log('5、微任务2')
  })
}, 0)
setTimeout(() => {
  console.log('6、宏任务2')
  Promise.resolve().then(function() {
    console.log('7、微任务3')
  })
}, 0)
Promise.resolve().then(function() {
  console.log('3、微任务1')
})
console.log('2、Script')

//1、Script
//2、Script
//3、微任务1
//4、宏任务1
//5、微任务2
//6、宏任务2
//7、微任务3


//最后这个练习打印的结果为1 2 3 4 5 6 7 8 9 10。
//其中有一个小坑就是promise本身是同步的,只有.then才是异步的微任务。
console.log(1)
setTimeout(function() {
    console.log(5)
    new Promise(function(resolve) {
        console.log(6)
        resolve()
    }).then(function() {
        console.log(7)
    })
})

new Promise(function(resolve) {
    console.log(2)
    resolve()
}).then(function() {
    console.log(4)
})

setTimeout(function() {
    console.log(8)
    new Promise(function(resolve) {
        console.log(9)
        resolve()
    }).then(function() {
        console.log(10)
    })
})
console.log(3)

以上为我个人在学习过程中的总结,欢迎大家一起学习讨论,如有不对,我非常乐意你能指出来。
最后非常感谢你的阅读。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值