Event Loop——事件循环(JS运行机制)
1.所有同步任务都在主线程上执行,形成一个执行栈
2.而异步任务会被放置到异步处理模块,当异步任务有了运行结果,就将该函数移入任务队列。
3.一旦执行栈中的所有同步任务执行完毕,引擎就会读取任务队列,然后将任务队列中的第一个任务压入执行栈中运行。 只要主线程空了,就会去读取任务队列,该过程不断重复,这就是所谓的事件循环。
宏任务和微任务
任务队列中的都是已经完成的异步操作,异步任务分为宏任务(macrotask) 与微任务 (microtask)。宏任务会进入一个队列,而微任务会进入到另一个不同的队列,且微任务要优于宏任务执行。
宏任务:script(整体代码)、setTimeout、setInterval、I/O、事件、postMessage、 MessageChannel、setImmediate (Node.js)
微任务:Promise.then、 MutaionObserver、process.nextTick (Node.js)
process.nextTick指定的异步任务总是发生在所有异步任务之前,因此先执行process.nextTick
放入任务队列的条件 ------ 1.改变状态 2.有回调函数
1
setTimeout(()=>{
console.log(1)
},0)
Promise.resolve().then(()=>{
console.log(2)
})
Promise.resolve().then(()=>{
console.log(4)
})
console.log(3)
//3 2 4 1
先执行同步任务,再执行微任务,最后执行宏任务。
2
setTimeout(()=>{
console.log(1)
},0)
new Promise((resolve)=>{
console.log(2) //执行器函数是同步执行的
resolve()
}).then(()=>{
console.log(3)
}).then(()=>{
console.log(4)
})
console.log(5)
//2 5 3 4 1
3
const first = () => (new Promise((resolve, reject) => {
console.log(3)
let p = new Promise((resolve, reject) => {
console.log(7)
setTimeout(() => {
console.log(5)
resolve(6)//会被忽略,因为状态已经改变过了,因为状态已经改变过了,因为状态已经改变过了!
}, 0)
resolve(1)
})
resolve(2)
p.then((arg) => {
console.log(arg)
})
}))
first().then((arg) => {
console.log(arg)
})
console.log(4)
//3 7 4 1 2 5
最先执行first(),先看它里面有无同步代码,所以先输出3,又因为里面的p也是同步执行的,所以7也是同步执行的(同2),接着打印4,接着启动定时器,将5放入宏任务(宏任务队列[5]),resolve(1)也是立即执行,意味着p的状态被改变了,p.then的回调函数先执行,将arg=1的放入微队列中(微任务队列[1]),然后执行resolve(2),将arg=2的放入微队列中(微任务队列[1,2]),然后执行微队列的任务,输出1,再输出2,再输出5。
4
setTimeout(() => {
console.log("0")
}, 0)
new Promise((resolve, reject) => {
console.log("1")
resolve()
}).then(() => {
console.log("2")
new Promise((resolve, reject) => {
console.log("3")
resolve()
}).then(() => {
console.log("4")
}).then(() => {
console.log("5")
})
}).then(() => {
console.log("6")
})
new Promise((resolve, reject) => {
console.log("7")
resolve()
}).then(() => {
console.log("8")
})
//1 7 2 3 8 4 6 5 0
开启定时器,将console.log("0")放入宏队列,然后同步执行console.log("1"),再执行resolve(),将console.log("2")...console.log("5")放入微队列,然后立马执行console.log("7"),将console.log("8")放入微队列。
初始化代码执行结果//1 7 宏任务队列[0] 微任务队列[2,8]
取出console.log("2")执行,立即执行console.log("3"),再调用 resolve(),console.log("4")进入微队列,虽然 console.log("5")不进入队列(console.log("4")还没有执行),但是函数已经有了结果,改变了状态,将console.log("6")放入微队列
执行结果//1 7 2 3 宏任务队列[0] 微任务队列[8,4,6]
取出console.log("8")执行,取出console.log("4")执行,将console.log("5")放入微队列,取出console.log("6")执行,取出console.log("5")执行,取出console.log("0")执行,