JavaScript是单线程的,所以有了同步异步的概念,异步如常见的setTimeout、Promise,其中setTimeout是宏任务,Promise是微任务。
同为宏任务的有setInterval等,同为微任务的有Node的process.nextTick等。 相比于宏任务,微任务的开销会小一点。
*JS代码执行顺序是,按主线程执行代码,当遇到一个宏任务或者微任务时,不会立即执行,而是分别先放到宏任务和微任务队列排队,然后先执行主线。
宏任务与微任务之间的执行顺序是怎样的呢?
当主线代码执行完毕后,开始执行异步任务。只要执行一个宏任务,首先就要先去检查微任务队列,如果有任务,就依次执行。执行完毕后,再开始执行宏任务 这就是所谓的事件循环。
一个宏任务进行中可以添加一些微任务。 就好像你在银行办业务,每一个办理业务的人就是柜员的一个宏任务。 而微任务则是每个人办的业务(宏任务)中的每个小业务(微任务)。前面大爷办完储蓄业务后,又想办理财业务,你的业务就需要继续往后推。 无论什么需求,只要柜员能办大爷办理的,都会在处理你的业务之前来做这些事情,这些都可以认为是微任务。 当前微任务没执行完之前,不会执行下一个宏任务。
经典面试题
setTimeout (() => { console.log(4)})
new Promise (resolve => {
resolve()
console.log(1)
}).then (() => {
console.log(3)
})
console.log(2)
setTimeout是宏任务来存在,而Promise.then则是具有代表性的微任务,而上述代码的执行顺序就是按照序号输出的。即打印1234。
所有会进入的异步都是指的事件回调中的那部分代码。
即new Promise在实例化的过程中所执行的代码都是同步进行的,而then中注册的回调才是异步执行的。在同步代码执行完成后才回去检查是否有异步任务完成,并执行对应的回调,而微任务又会在宏任务之前执行。
多提一嘴asyns/await函数
因为, async/await本质上还是基于 Promise的一些封装,而 Promise是属于微任务的一种。所以在使用 await关键字与 Promise.then效果类似:
setTimeout(_ =>console.log(4))
async function main() {
console.log(1)
await Promise.resolve()
console.log(3)
}
console.log(2)
async函数在await之前的代码都是同步执行的,可以理解为await之前的代码属于 newPromise时传入的代码,await之后的所有代码都是在 Promise.then中的回调。
宏任务
I/O、setTimeout、setInterval、setImmediate、requestAnimationFrame
微任务
process.nextTick、MutationObserver、Promise.then catch finally
demo
console.log('1');
setTimeout(function() { //宏任务
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() { //微任务
console.log('6');
})
new Promise(function(resolve) {
console.log('7'); //宏任务
resolve();
}).then(function() { //微任务
console.log('8')
})
此段代码会打印出: 1 7 6 8 2 4 3 5
-
首先打印1。继续执行,遇到setTimeout,全部放到宏任务队列里。然后process.nextTick,放到微任务队列。然后打印7。 (注意这里是new Promise后立即执行的函数,不是微任务)然后将then后的任务放到微任务队列。 现在宏任务队列里有 setTimeout,微任务队列里 process.nextTick、then。
-
继续执行,在执行宏任务之前,检查微任务队列,依次执行。打印:6 8。
-
可以执行宏任务了,执行setTimeout,打印2。
-
接着执行,遇到process.nextTick,放到微任务队列。
-
执行 new Promise,打印4. 将then放入微任务。现在微任务队列里 process.nextTick、then
-
依次执行,打印3、5。