宏任务、微任务小记

126 阅读5分钟

宏任务、微任务有哪些

  • 宏任务:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
  • 微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)

宏任务、微任务是怎么执行的

先执行同步代码,遇到宏任务则将宏任务放入宏任务队列中,遇到微任务则将微任务放入微任务队列中,当所有同步代码执行完毕后,再将微任务从队列中调入主线程执行,微任务执行完毕后再将宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕

注意: 宏任务进入任务队列后就已经在开始执行, 并不是当执行到宏任务时才开始执行, 看下面代码

function sleep(time){
 var timeStamp = new Date().getTime();
 var endTime = timeStamp + time;
 while(true){
 if (new Date().getTime() > endTime){
  return;
 } 
 }
}

console.log(1)

setTimeout(()=>{console.log(2);},2000)

sleep(2000)

setTimeout(()=>{console.log(3);},1000)

sleep(2000)
console.log(4)

含promise的代码段

new Promise(() => {}).then() ,我们来看这样一个Promise代码

前面的 new Promise() 这一部分是一个构造函数,这是一个同步任务

后面的 .then() 才是一个异步微任务,这一点是非常重要的

含async await的代码段

async/await本质上还是基于Promise的一些封装,而Promise是属于微任务的一种 所以在使用await关键字与Promise.then效果类似

await 后面可以跟任何的JS 表达式。虽然说 await 可以等很多类型的东西,但是它最主要的意图是用来等待 Promise 对象的状态被 resolved。

要注意的是如果 await 的是 promise对象,await 会暂停 async 函数内后面的代码,先执行 async 函数外的同步代码,等着 Promise 对象 fulfilled,然后把 resolve 的参数作为 await 表达式的运算结果返回后,再继续执行 async 函数内后面的代码

例子1

setTimeout(function(){
    console.log(1);
});
new Promise(function(resolve){		    
    console.log(2);
    resolve();
}).then(function(){		    
    console.log(3);
}).then(function(){
    console.log(4)
}); 		
console.log(5);
// 2 5 3 4 1
  • 遇到setTimout,异步宏任务,放入宏任务队列中
  • 遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
  • Promise.then,异步微任务,将其放入微任务队列中
  • 遇到同步任务console.log(5);输出5;主线程中同步任务执行完
  • 从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
  • 从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空

例子2

setTimeout(()=>{
    new Promise(resolve =>{
        resolve();
    }).then(()=>{
        console.log('test');
    });
    console.log(4);
});
new Promise(resolve => {
    resolve();
    console.log(1)
}).then( () => {
    console.log(3);
    Promise.resolve().then(() => {
        console.log('before timeout');
    }).then(() => {
        Promise.resolve().then(() => {
          console.log('also before timeout')
        })
    })
})
console.log(2);
//1,2,3,before timeout,also before timeout,4,test;
  • 遇到setTimeout,宏任务,放入宏任务队列中;
  • 遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出1;
  • 而Promise.then,微任务,将其放入微任务队列中
  • 遇到同步任务console.log(2),输出2;主线程中同步任务执行完
  • 从微任务队列中取出任务到主线程中,输出3,此微任务中又有微任务,Promise.resolve().then(微任务a).then(微任务b),将其依次放入微任务队列中;
  • 从微任务队列中取出任务a到主线程中,输出 before timeout;
  • 从微任务队列中取出任务b到主线程中,任务b又注册了一个微任务c,放入微任务队列中;
  • 从微任务队列中取出任务c到主线程中,输出 also before timeout;微任务队列为空
  • 从宏任务队列中取出任务到主线程,此任务中注册了一个微任务d,将其放入微任务队列中,接下来遇到输出4,宏任务队列为空
  • 从微任务队列中取出任务d到主线程 ,输出test,微任务队列为空

例子3

console.log(1)
setTimeout(function() {
    console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
    resolve(4)
})
p.then(data => {
    console.log(data)
})
console.log(3)
//1,3,4,2
  • 遇到同步任务console.log(1);输出1;
  • 遇到setTimeout 异步宏任务,放入宏任务队列中;
  • 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,但由于new Promise没有输出事件,所以接着执行遇到.then;
  • 执行.then,异步微任务,被分发到微任务Event Queue中;
  • 遇到同步任务console.log(3);输出3;
  • 主线程中同步任务执行完,从微任务队列中取出任务到主线程中,p.then 输出4,微任务执行完毕,任务队列为空;
  • 开始执行宏任务setTimeout 输出2,宏任务队列为空;

例子4

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

new Promise(function(resolve) {
    console.log(5)
    resolve()
}).then(function() {
    console.log(6)
})

setTimeout(function() {
    console.log(7)
    new Promise(function(resolve) {
        console.log(8)
        resolve()
    }).then(function() {
        console.log(9)
    })
})
console.log(10)
//1,5,10,6,2,3,4,7,8,9
  • 遇到同步任务console.log(1);输出1;
  • 遇到setTimeout 异步宏任务,放入宏任务队列中;
  • 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出5,所以接着执行遇到.then;
  • 执行.then,异步微任务,被分发到微任务Event Queue中;
  • 遇到setTimeout,异步宏任务;放入宏任务队列中;
  • 遇到同步任务console.log(10);输出10,主线程中同步任务全部执行完;
  • 从微任务队列中取出任务到主线程中,输出6;
  • 在从宏任务队列中取出任务到主线程中,执行第一个setTimeout,输出2,3,4(在宏任务中执行同步,同步,异步微任务);
  • 在执行第二个setTimeout,输出7,8,9(和8同理);