JavaScript的宏任务和微任务有哪些以及怎样执行的详解
作者:宋小土
前言
Javascript 的执行顺序,众所周知是按照顺序自上而下执行。
Javascript任务分为同步任务和异步任务,异步任务又分为宏任务和微任务,其中异步任务属于耗时的任务。
一、宏任务和微任务是什么?
首先,js 是单线程语言,通俗理解就是,只有一个主线程,那么在任务多的情况下,就会出现死锁等问题
单线程的局限性
单线程的局限性在于阻塞,如果一个任务耗时很长(如同步的无限循环或大量计算),会阻塞后续所有任务,导致页面“卡死”。
那么如何解决单线程的缺陷?
JavaScript 通过 事件循环(Event Loop)实现非阻塞行为,意思是主线程会不断检查调用栈和任务队列,当调用栈为空时,从任务队列中取出回调任务执行。
在 JavaScript 的事件循环(Event Loop)机制中,先执行同步任务,再执行异步任务,而异步任务分成了宏任务和微任务,宏任务和微任务决定了异步代码的执行顺序。它们的区别在于执行优先级和触发时机。
1.1宏任务
宏任务代表较大的、独立的异步任务,由浏览器或 Node.js 安排在不同的任务队列中执行。
每次事件循环(Event Loop)会执行一个宏任务,然后检查并执行所有微任务。
1.2.微任务
微任务是比宏任务更小的任务,通常用于处理高优先级的异步操作。
每当一个宏任务执行完毕,JS 引擎会立即清空整个微任务队列,然后再执行下一个宏任务。
二、宏任务、微任务是怎么执行的?
首先,是先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
注意:
这里容易错误的认为:就是微任务先于宏任务执行!
其实不是,要明确:
1、那就是事件循环是从第一个宏任务开始!即:script 中的代码,script 标签(或模块)的全局代码是一个宏任务!
2、浏览器加载并执行script 中的代码时,这些同步代码(如 console.log、变量声明、函数调用等)会被视为第一个宏任务。
3、同步代码执行完后,才会处理微任务和其他宏任务
4、在 script 宏任务执行期间,遇到的 Promise.then、MutationObserver 等微任务会被收集到微任务队列。
5、同步代码执行完毕后,立即清空微任务队列,然后才会处理下一个宏任务(如 setTimeout)。
宏任务会先进入主线程 但是执行的时候会先执行该宏任务当中的同步任务 然后执行该宏任务中的微任务
当前宏任务执行完毕后、下一个宏任务开始前,JavaScript 会清空所有微任务队列。导致看上去是微任务比宏任务先执行!
要理解事件循环的关键在于区分“加入队列”和“执行”这两个步骤,以及明白微任务队列的优先级高于宏任务队列。 虽然宏任务可能先进入队列,但微任务会在当前宏任务执行完毕后、下一个宏任务执行前被优先执行。
三、举个栗子
我举个栗子,让你明白执行顺序的问题
首先要明确 ,script中的整体代码是最先执行的任务!
宏任务:像你的“主要待办事项”,
比如:
今天要写作业(script主代码)
1小时后取快递(setTimeout)
明天去体检(setInterval)
微任务:像“主要事项完成后立刻要做的小事”,
比如:
写完作业后立刻收拾书包(Promise.then)
取完快递后立刻拆包装(MutationObserver)
console.log("开始写作业"); // 宏任务1(大事) setTimeout(() => { console.log("1小时后:取快递"); // 宏任务2(下一件大事) }, 0); Promise.resolve().then(() => { console.log("写完作业后:收拾书包"); // 微任务(小事) });
那么执行顺序是:
先执行第一个宏任务(script整体代码),输出 开始写作业 和 作业写完了。
立刻执行这个宏任务产生的微任务(收拾书包)。
最后执行下一个宏任务(取快递)。
四、宏任务、微任务有哪些?
4.1宏任务
4.2 微任务
注意:
new Promise(…)是构造函数,是同步代码。
五、案例
<script> console.log('开始'); // 宏任务1 //宏任务A setTimeout(() => { console.log("宏任务A"); Promise.resolve().then(() => console.log("微任务A")); }, 0); //宏任务B setTimeout(() => { console.log('宏任务B'); }, 0); Promise.resolve().then(() => { console.log('微任务'); }); </script> //开始 微任务 宏任务A 微任务A 宏任务B
输出过程:
代码执行的时候,先执行全局代码中的同步代码 输出:开始
然后执行 全局代码中的微任务 输出 微任务
微任务执行完毕后主线程会检查调用栈把宏任务A调入进行执行
但是会先执行宏任务A中的同步任务会先输出, 输出 宏任务A
然后输出微任务A,等到宏任务A全部执行完毕后主线程会检查调用栈调用栈会把 宏任务B调入进行执行
但是宏任务B中没有微任务 所以直接输出 宏任务B这里我解释一下 为什么 宏任务A比微任务A先输出 ,因为宏任务A是该宏任务中的同步任务 所以先执行输出
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
console.log("Script start"); setTimeout(() => { console.log("setTimeout"); }, 0); Promise.resolve() .then(() => { console.log("Promise 1"); }) .then(() => { console.log("Promise 2"); }); console.log("Script end"); 、 //Script start //Script end //Promise 1 //Promise 2 //setTimeout
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
总结
到此这篇关于JavaScript的宏任务和微任务有哪些以及怎样执行的文章就介绍到这了,更多相关js宏任务和微任务内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!