面试也是一个增长知识点的过程。就像我昨天面试时被问到一个没有听过的名词:简单阐述一下宏任务和微任务,不知道答案的我直接就被问住了,今天在网上翻阅了很多文章终于算是弄懂了,也自己总结一下。
首先,我们都知道JavaScript 是一个单线程、异步、非阻塞的语言,那我们怎么来理解这句话呢,先看一段代码:
console.log(1);
setTimeout(function() {
console.log(2);
}, 0);
console.log(3);
代码最终依次输出1,3,2。
在上段代码中,setTimeout被JavaScript引擎通过事件循环Event Loop机制放入了任务队列中,所以主线程最优先放入执行栈内执行,1,3按顺序执行完毕后,执行栈任务完成,再从任务队列中取出2执行,故2最后输出。
先来看一段关于任务队列的描述:
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。
在面试时关于上面为止的知识点我还是知道的,那宏任务和微任务又是什么鬼呢?看了很多的文章这里我可以先简单总结为“凡事都有轻重缓急,微任务的优先度高于宏任务,所以大家都在排队的情况下JavaScript会优先执行微任务”,当然详细的后面再慢慢说。
再看一段代码:
new Promise(function(resolve) {
setTimeout(function() {
console.log(1);
}, 0);
resolve();
console.log(2);
}).then(function() {
console.log(3);
});
console.log(4);
代码最终依次输出2,4,3,1。
一开始我对这个结果是很疑惑的,明明都是异步,setTimeout先进队列理论应该是setTimeout先执行啊,按这个原则来讲应该是2,4,1,3才对,所以这里就是宏任务和微任务的区别。
在JavaScript引擎的任务队列又被划分为macrotask(宏任务) 和 microtask(微任务)。宏任务与微任务的执行关系为:

画的比较乱,但大概就是这么个流程。微任务必然是在主线程或某个宏任务执行的时候创建的,在上一个宏任务执行完成后,会执行当前微任务队列中的所有微任务,之后再去执行一个宏任务如此反复。
再来看一下宏任务、微任务分类有哪些:
宏任务包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
微任务包括: Promises, Object.observe, MutationObserver。
所以也是解开了我心目中的疑团,因为Promise属于微任务,第一次执行的代码即为主线程,属于宏任务,一个宏任务执行完成后就会去执行所有的微任务。这也就是为什么Promise的then总会显示在setTimeout之前的原因了(new Promise传入的函数当然还是属于主线程中同步执行的)。但是在node.js中的任务队列会机制和浏览器有一点点不同,这里也不去深究了。
1057

被折叠的 条评论
为什么被折叠?



