js事件循环之宏任务与微任务
- 执行环节node 11,与js文件执行结果可能会有部分差异
-
javascript中的事件循环
javascript是单线程执行的,线程中的任务是按照顺序来执行,如果某个任务执行耗时较长,后面的任务就需要排队等待。任务分类:
- 同步任务
- 异步任务
解读:
- 同步任务与异步任务分别进入不同的执行场所(线程),同步任务进入主线程,异步任务进入Event Table(事件表)并进行函数注册
- 当Event Table中的异步函数完成后,会在Event Queue(事件队列)中注册回调函数
- 当主线程中的任务执行完毕后,会去Event Queue中读取对应的函数,推入主线程执行
- js解析器会不断的存放检查主线程执行栈是否为空,然后重复第三部操作,这个过程也就是我们常说的Event loop(事件循环)
-
宏任务与微任务
宏任务:script(主程序代码),setTimeout,setInterval,setImmediate(node),I/O操作,UI交互,postMessage,MessageChannel...
微任务:promise(原生promise,有些包装后的promise为宏任务),process.nextTick(),Object.observe,MutaionObserver...
区别:宏任务与微任务都是异步任务,都在同一个队列(进程)中,主要区别在于他们的执行顺序,Event loop的走向和取值
解读:
- 在异步环境中,宏任务在微任务之前执行。遇到宏任务,会先将宏任务放入Event Queue中,然后再执行微任务,将微任务放入Event Queue中。
- 宏任务与微任务对应的Event Queue不是同一个Event Queue,在往外拿(把回调函数推入主线程执行)的过程中,先拿微任务Event Queue中的回调函数,再拿宏任务Event Queue中的回调函数
- 执行过程:异步线程中 => 主线程中任务执行为空(执行栈为空) => 宏任务进Event Queue => 微任务进Event Queue => 微任务回调函数出Event Queue进主线程 => 宏任务回调函数出Event Queue进主线程 => 主线程中任务执行为空(执行栈为空) => ... => Event loop事件循环重复执行过程(判断主线程任务执行结束,把异步任务推到主线程的循环执行过程) => ... => 所有任务执行结束
- 实例
// 实例1
console.log(1)
new Promise((resolve, reject) => {
console.log(2)
resolve()
console.log(3)
reject()
console.log(4)
})
.then(() => {
console.log(5)
})
console.log(6)
// 1, 2, 3, 4, 6, 5
原因:
- promise构造函数里面是同步执行的
- then方法指向的回调将会在当前线程同步任务执行完之后执行
- 结果就是1,2,3,4,6,5
console.log(1)
setTimeout(() => console.log(2), 0) // fn1
setTimeout(() => console.log(3), 1000) // fn2
new Promise((resolve) => { // fn3
console.log(4)
resolve()
})
.then(() => console.log(5)) // fn4
setTimeout(() => console.log(6), 0) // fn5
console.log(7)
// 1, 4, 7, 5, 2, 6, 3
原因:
- promise构造函数里面是同步执行的
- then方法指向的回调将会在当前线程同步任务执行完之后执行
- consoel.log()方法,promise构造函数会放到同步线程中,输出1 => fn3 => 4 => 7,,promise().then()方法,setTimeout函数会放在异步线程中去,setTimeout为宏任务,promise().then()为微任务,因此先执行fn4 => 5 => fn1 => fn2 => fn5,其中fn2延迟比fn5时间长,因此fn5会先返回,输出为fn4 => 5 => fn1 => 2 => fn5 => 6 => fn2 => 3
console.log(1)
setTimeout(() => console.log(2), 0) // fn1
console.log(3)
setTimeout(() => console.log(4), 1000) // fn2
Promise.resolve()
.then(() => { // fn3
setTimeout(() => console.log(5), 0) // fn4
setTimeout(() => console.log(6), 1000) // fn5
console.log(7)
Promise.resolve()
.then(() => console.log(8)) // fn6
})
.then(() => console.log(9)) // fn7
new Promise((resolve) => { // fn8
console.log(10)
resolve()
}).then(() => { // fn9
console.log(11)
})
setTimeout(() => console.log(12), 0) // fn10
console.log(13)
setTimeout(() => console.log(14), 1000) // fn11
// 结果
// 1, 3, 10, 13, 7, 11, 8, 9, 2, 12, 5, 4, 14, 6
原因:
- promise构造函数里面是同步执行的
- then方法指向的回调将会在当前线程同步任务执行完之后执行
- 同上原因,1,3,fn8,13同步执行,输出:1 => 3 => fn8 => 10 => 13,主线程执行结束。微任务开始执行:然后在执行promise().then()方法,fn3在fn9前面定义,输出:fn3 => 7 => fn9 => 11,主线程执行结束。执行fn3中定义的promise().then()方法,再执行fn3本身的then()方法,输出:fn6 => 8 => fn7 => 9,主线程执行结束。宏任务开始执行:fn1 => fn2 => fn10 => fn11 => fn4 => fn5,然后通过延迟时间,输出:fn1 => 2 => fn10 => 12 => fn4 => 5 => fn2 => 4 => fn11 => 13 => fn5 => 6
- 输出:1 => 3 => fn8 => 10 => 13 => fn3 => 7 => fn9 => 11 => fn6 => 8 => fn7 => 9 => fn1 => 2 => fn10 => 12 => fn4 => 5 => fn2 => 4 => fn11 => 14 => fn5 => 6
- 最终结果:1, 3, 10, 13, 7, 11, 8, 9, 2, 12, 5, 4, 14, 6
参考文章