可能很多同学都分不清微任务和宏任务,网上的文章也很多,这次我来试着以我的理解来阐述一下。
我们现在看看他们分别有哪些?
同步任务指的是在主线程上排队执行的任务,例如:
- 整体代码script。
异步任务指的是不进入主线程、而进入“任务队列”的任务,例如:
- setTimeout。
- setInterval。
- Promise。
- process.nextTick。
JavaScript把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
宏任务(macrotask)和微任务(microtask)。
宏任务常见的有:setTimeout,setInterval,script(最外层的script标签)等。
微任务常见的有:Promise.then、.catch和.finally,process.nextTick等。
这是对于这些任务的解释和常见案例,那么他们是以什么顺序执行的呢?
我们直接以实际案例看一下:
这个输出顺序是怎么样的呢?
现在我们来仔细看看,console.log(111)和console.log(888)最先执行,因为他们是同步任务。另外四个setTimeout属于宏任务,Promise.resolve()属于微任务。
第三个执行的是console.log(777),说明同步任务完成之后会立即执行微任务,而不是宏任务,这就是所说的微任务插队。
第四个是console.log(222)说明第一个宏任务setTimeout开始执行了,同样的还是先执行完了里面的同步任务console.log(555),紧跟着就是微任务console.log(333),但是console.log(444)没有执行,那么去哪里了呢?先不着急先看后面。到这里第一个宏任务setTimeout就执行完成了。
然后执行了console.log(666)说明第二个宏任务setTimeout执行了,紧接着执行的是console.log(444),说明刚才第一个setTimeout宏任务的执行的时候遇到宏任务3的时候没有立即执行,而是放在任务队列的最后,这里的宏任务4继续放在了最后,就在宏任务3之后去了。
最后执行了console.log(999),这是最后一个宏任务。
由此我们可以得出一个结论,同步任务最先执行,由上而下执行的过程中如果遇到异步任务就放在任务队列最后排队,同步任务执行完成之后,优先执行所有的微任务,微任务执行完成之后,按顺序执行宏任务,如此反复循环,事件循环。
同步->微任务->宏任务,tips:微任务立即执行,宏任务放在任务队列最后等待下一次主线程空闲后执行
上面提到了的任务队列和事件循环,任务队列可以当做是一个个的回调任务,当主线程的任务完成后,就开始执行任务队列中的任务(如果当前任务队列中再添加了新的异步任务,则其回调函数会放在之后的任务队列中)。异步任务执行后,其回调会放到任务队列中。当主线程任务执行结束后,就去任务队列中捞接下来要做的任务,放到主线程中执行,直到任务全部结束。如果无新的任务可做,浏览器处于等待状态,知道新的外部输入、事件触发,这样一个循环过程称为事件循环。
学会了吗?学会了可以试试下面这个?看是怎么输出的。