JavaScript宏任务、微任务

108 阅读4分钟

前言

关于Javascript的执行顺序,众所周知是按照顺序自上而下执行。但是在我们面试过程中。总会遇到面试官问:这段代码是如何执行的,输出结果是怎样的,然后再讲下为什么。对于这种问题,难的不是输出什么,而是为什么,输出结果我们可以像做选择题一样输出,但是为什么才是问题的关键,但我们很少能够答道点上。

为什么会有同步异步

JavaScript是单线程执行的语言,在同一时间只能做一件事。这就导致后面的任务需要等到前面的任务执行完成才能执行。如果前面的任务执行很耗时,就会造成后面的任务一直等待。为了解决这个问题JavaScript中出现了同步任务异步任务

同步任务

主线程上排队执行的任务。只有前一个任务执行完成,后一个任务才能执行,形成了一个执行栈。

异步任务

不进入主线程,而是进入任务队列。当主进程中的任务执行完毕,就会从任务队列中取出任务放进主线程中来执行。

事件循环

由于主线程一直不断的重复获得任务、执行任务,再获取再执行,这种机制就叫做事件循环(Event Loop)

总结

  • 所有同步任务都在主线程上执行,形成一个执行栈。
  • 除了主线程外,还有个任务队列。
  • 主线程完成所有任务(执行栈清空),就会读取任务队列,先执行微任务再执行宏任务。
  • 以上三步会不停的重复。

先看一道面试题,输出结果???

carbon.png

为什么300200先输出???这就涉及到宏任务和微任务。

宏任务

setTimeout
setInterval
Ajax
DOMs事件

微任务

Promise
async/await

在了解执行顺序之前我们先记住一点:微任务执行时机比宏任务要早

ES6中,Microtask称为jobs(微任务)、Macrotask称为task(宏任务),宏任务是由宿主(浏览器、node)发起的,而微任务是由JavaScript自身发起的。

note宏任务微任务
任务发起宿主(node、浏览器)JS引擎
事件1.script
2.setTimeout/setInterval
3. UI rendering/UI事件
4. postMessage,MessageChannel
5. setImmediate,I/O(Node.js)
1. Promise
2. MutaionObserver
3. Object.observe(已废弃;Proxy 对象替代)
4. process.nextTick(Node.js)
运行顺序后运行先运行
会触发新一轮的Tick吗?不会

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

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

案例、练习

carbon (2).png

解析

1.遇到setTimout,异步宏任务,放入宏任务队列中
2.遇到new Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出2
3.而Promise.then中注册的回调才是异步执行的,将其放入微任务队列中
4.遇到同步任务console.log(‘5’);输出5;主线程中同步任务执行完
5.从微任务队列中取出任务到主线程中,输出3、 4,微任务队列为空
6.从宏任务队列中取出任务到主线程中,输出1,宏任务队列为空

案例 carbon (3).png

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