前言
学习事件循环,首先我们需要知道 javascript 是一个单线程语言,单线程?什么是单线程呀,可以理解为排队做核酸,而且是几百人只有一条队,人们只能一个接着一个去排队做核酸,显然,这种机制存在很大的弊端,如果我们有一个任务卡死了,后面的所有任务都无法被执行了。或者有某个任务耗时很长,那么也会导致后面的任务被延迟执行。
概念
任务类型
有的小伙伴可能还不了解什么是任务,以下是mdn的概念
一个任务就是指计划由标准机制来执行的任何 JavaScript,如程序的初始化、事件触发的回调等。除了使用事件,你还可以使用
setTimeout()或者setInterval()来添加任务**。** 任务队列和微任务队列的区别很简单,但却很重要:
当执行来自任务队列中的任务时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个任务。在每次迭代开始之后加入到队列中的任务需要在下一次迭代开始之后才会被执行.
每次当一个任务退出且执行上下文为空的时候,微任务队列中的每一个微任务会依次被执行。不同的是它会等到微任务队列为空才会停止执行——即使中途有微任务加入。换句话说,微任务可以添加新的微任务到队列中,并在下一个任务开始执行之前且当前事件循环结束之前执行完所有的微任务。 javascript 中的任务被分为 同步任务 和 异步任务
同步任务:在主线程上排队等待被执行的任务,只有一个任务执行完毕,才能执行下一个任务。
异步任务:不进入主线程,而是放在任务队列中,若有多个异步任务则需要在任务队列中排队等待,等待主线程中的任务全部清空,把任务队列取出已完成的任务放入执行栈中执行。
常见的宏任务和微任务:
宏任务:script(整体代码)、setTimeout、setInterval
微任务:promise、async await
执行栈和任务队列
1、执行栈:它是一个存储函数调用的栈结构,遵循先进后出的原则。它主要负责跟踪所有要执行的代码,每当一个函数执行完成时,就会从堆栈中弹出(pop)该执行函数;如果有代码需要进行执行的话,就进行push操作,
function foo(b){
var a = 5 ;
return a*b +10;
}
function bar(x){
var y=3;
return foo(x*y);
}
console.log(bar(6));
执行上述代码,首先把方法bar(6)放入执行栈,然后把foo(18),foo(18)(100)把计算结果返回给bar(6)(100),输出100
当foo执行完毕然后返回,foo就被弹出栈(剩下bar函数的调用)。当bar也执行完毕然后返回时,bar也被弹出,栈就被清空了。
2、任务队列:它使用到的是数据结构中的队列结构,用于保存异步任务,遵循先进先出的原则,它主要负责将新的任务发送到队列中进行处理
javascript 在执行代码时,会将同步的代码按照顺序排在执行栈中,然后依次执行里面的函数,当遇到异步任务时,将其放入任务队列中,等待当前执行栈中所有同步代码执行完毕,就会将异步任务队列中取出已完成的任务的回调并将其放入任务队列中继续执行,如此循环往复,直至执行完所有任务
javascript代码自上往下执行,遇到同步任务代码便将其放入执行栈中等待执行,遇到异步任务代码,宏任务便将其放入宏任务队列,微任务就将其放入微任务队列,首先执行同步代码,再执行微任务代码,若宏任务代码中有微任务,等待任务了的微任务完全执行完毕,再执行下一个宏任务
console.log('同步代码1')
setTimeout(() => {
console.log('setTimeout')
new Promise((resolve) => {
resolve()
}).then(() => {
console.log('promise.then1111')
})
new Promise((resolve) => {
resolve()
}).then(() => {
console.log('promise.then222')
})
}, 0)
setTimeout(() => {
console.log('setTimeout333')
new Promise((resolve) => {
resolve()
}).then(() => {
console.log('promise.then4444')
})
}, 0)
new Promise((resolve) => {
console.log('同步代码2')
resolve()
}).then(() => {
console.log('promise.then')
})
console.log('同步代码3')
同步代码1
同步代码2
同步代码3
promise.then
setTimeout
promise.then1111
promise.then222
setTimeout333
promise.then4444
当执行来自任务队列中的任务时,在每一次新的事件循环开始迭代的时候运行时都会执行队列中的每个任务。在每次迭代开始之后加入到队列中的任务需要在下一次迭代开始之后才会被执行.
每次当一个任务退出且执行上下文为空的时候,微任务队列中的每一个微任务会依次被执行。不同的是它会等到微任务队列为空才会停止执行——即使中途有微任务加入。换句话说,微任务可以添加新的微任务到队列中,并在下一个任务开始执行之前且当前事件循环结束之前执行完所有的微任务。
总结
javascript 代码是单线程语言,代码自上而下执行,每一个宏任务为一个循环,首先会执行第一个整体(script)宏任务,当这个宏任务里面的所有微任务执行完毕,再开始下一个宏任务