javascript进阶知识24 - 宏任务与微任务

137 阅读3分钟

前面我们说了浏览器的事件循环,也就是浏览器执行代码的顺序,知道了浏览器是先运行同步代码,再运行异步代码。但其实,浏览器的事件循环里,异步代码也是分为了两类,一类是微任务,一类是宏任务。

微任务

微任务的优先级高于宏任务,也就是说,当浏览器执行完同步代码后,浏览器会先去微任务队列中查找微任务,如果有微任务的话就将这个任务出列,进入执行栈中,执行代码。等到微任务队列中没有任务之后,才会去宏任务队列中进行查找。

js里的微任务一般就是PromiseObject.observeMutationObserver,我们重点会Promise就可以了。

宏任务

宏任务是优先级最低的,必须等同步代码和微任务队列中都执行完毕后,才会执行宏任务队列中的代码。宏任务一般包括AJAXsetTimeoutsetInterval、DOM事件等。

例子:(一道面试题)

async function async1() {
    console.log('async1 start');
    await async();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(()=>{
    console.log('setTimeout')
},0)
async1();
new Promise(function(resolve){
    console.log('promise1');
    resolve();
}).then(function(){
    console.log('promise2')
});
console.log('script end');

它的输出结果是什么呢?其实看它的输出结果也就是看这段代码的运行先后顺序。

步骤:

  • 前面是声明函数,没有输出。
  • 执行console.log('script start')输出'script start';
  • 发现是定时器,将它放入宏任务中;
  • 执行async1函数
    • 执行console.log('async1 start'), 输出'async1 start'
    • 执行await后面,也就是async2();
      • 执行async2()
      • 执行console.log('async2'), 输出'async2';
    • await就是.then操作,属于异步,且是微任务,放入微任务队列中;
    • 退出async1函数
  • 执行new Promise
    • 执行console.log('promise1'), 输出'promise1';
    • resolve();
    • .then属于异步,放入微任务队列中;
    • 退出promise
  • 执行console.log('script end'),输出'script end'
  • 同步代码执行完毕,下面去扫描微任务队列
    • 发现了async1函数中的await;
    • await出列,放入执行上下文栈中执行
    • 执行await,这里也就是等于p.then()(p就是async2()的返回值)
    • 执行console.log('async1 end'),输出'async1 end'
  • 继续扫描微任务队列
    • 发现.then(function(){console.log('promise2')});
    • 将它出列,放入执行上下文栈中执行;
    • 执行'console.log('promise2')',输出'promise2'
  • 继续扫描微任务队列,发现没有,去扫描宏任务队列
    • 发现setTimeout()
    • 将它出列,放入执行上下文栈中执行;
    • 执行console.log('setTimeout'),输出'setTimeout'
  • 执行完毕 image.png

其实这个还是挺简单的吧,搞懂原理,按照顺序就行了!

注:像异步的setTimeout和ajax请求之类的,它不是将源代码直接放入异步队列中,而是先放入它对应的那个执行器中,比如setTimeout就是放入执行定时器那个队列中中,然后开始计时,再将回调任务放入异步队列中,等到同步代码执行完后,再将回调放入执行栈中。这样形成一个类似多线程的操作。

如果有总结错误的地方,请大家多多指教,我也还在开始摸索js的底层,哈哈!!