终于搞懂 JavaScript 宏任务与微任务了,再也不怕面试被问到了

JavaScript 的宏任务与微任务老生常谈的问题了,特别是在面试过程当中,更是频繁被问到,所以有必要好好深入了解一下,不仅对我们自身的编程有帮助,面试也不会犯怵,哈哈。
javascript 事件循环
js 是单线程,就像学生排队上厕所,学生需要排队一个一个上厕所,同理 js 任务也要一个一个顺序执行。如果一个任务耗时过长,那么后一个任务也必须等着。
简单的说就是:js 只有一条通道,那么在任务多的情况下,就会出现拥挤的情况,这种情况下就产生了 ‘多线程’ ,但是这种“多线程”是通过单线程模仿的,也就是假的。
这意味着它一次只能执行一个任务。但是,通过事件循环(Event Loop)机制,可以处理异步操作并确保程序的高效运行。理解事件循环对于我们前端开发者来说至关重要!
事件循环的基本概念
事件循环的核心在于任务队列(Task Queue),它负责管理和调度各种任务的执行。任务分为两类:宏任务、微任务。
事件循环的执行步骤
事件循环的基本执行步骤如下:
- 执行一个宏任务(例如,从头到尾运行一个 script)。
- 执行所有的微任务(一直执行,直到微任务队列为空)。
- 更新渲染(如果需要)。
- 继续执行下一个宏任务。
看不懂没关系,结合下面的例子就能明白了!
宏任务和微任务详解
基于上面的基础知识,我们详细介绍下宏任务和微任务
为什么要区分宏任务和微任务?
(1)js 是单线程的,但是分同步异步
(2)微任务和宏任务皆为异步任务,它们都属于一个队列
(3)宏任务一般是:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
(4)微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
(5)先执行同步再执行异步,异步遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务
宏任务、微任务有哪些?
- 宏任务一般是:
script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境) - 微任务:
Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
宏任务、微任务是怎么执行的?
简单来说,宏任务、微任务的执行顺序是:先执行同步代码,异步任务放入任务队列中,当所有同步代码执行完毕后,先执行微任务在执行宏任务。
接下来,我们将通过很多例子,帮助大家深入了解
基本事件循环
例子 1:
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
代码分析:
setTimout是异步宏任务,放入宏任务队列中new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出 2Promise.then是异步微任务,将其放入微任务队列中- 遇到同步任务
console.log(5);输出 5;主线程中同步任务执行完 - 从微任务队列中取出任务到主线程中,输出 3、 4,微任务队列为空
- 从宏任务队列中取出任务到主线程中,输出 1,宏任务队列为空
例子二:
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(4)
})
p.then(data => {
console.log(data)
})
console.log(3)
//1,3,4,2d
代码分析:
- 遇到同步任务
console.log(1);输出 1; - 遇到
setTimeout异步宏任务,放入宏任务队列中; - 遇到
Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,但由于new Promise没有输出事件,所以接着执行遇到.then; - 执行
.then,异步微任务,被分发到微任务 Event Queue 中; - 遇到同步任务
console.log(3);输出 3; - 主线程中同步任务执行完,从微任务队列中取出任务到主线程中,
p.then输出 4,微任务执行完毕,任务队列为空; - 开始执行宏任务
setTimeout输出 2,宏任务队列为空;
嵌套的宏任务与微任务
例子一:
console.log('脚本开始')
setTimeout(() => {
console.log('这是 setTimeout1-------------');
Promise.resolve().then(() => {
console.log('这是 promise1');
});
}, 0);
setTimeout(() => {
console.log('这是 setTimeout2');
}, 0);
Promise.resolve().then(() => {
console.log('这是 promise2');
});
console.log('脚本结束');
执行顺序:
console.log('脚本开始')—— 同步代码,立即执行。- 第一个
setTimeout回调注册到宏任务队列。 - 第二个
setTimeout回调注册到宏任务队列。 Promise回调注册到微任务队列。console.log('脚本结束')—— 同步代码,立即执行。- 当前宏任务执行完毕,执行微任务队列:
console.log('这是 promise2')
- 微任务执行完毕,执行第一个宏任务队列:
console.log('这是 setTimeout1-------------')- 注册新的微任务
console.log('这是 promise1')到微任务队列。
- 执行微任务队列:
console.log('这是 promise1')
- 执行第二个宏任务队列:
console.log('这是 setTimeout2')
输出结果:
脚本开始 脚本结束 这是 promise2 这是 setTimeout1------------- 这是 promise1 这是 setTimeout2
多个微任务
console.log('脚本开始');
setTimeout(() => {
console.log('这是 setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('这是 promise1');
}).then(() => {
console.log('这是 promise2');
}).then(() => {
console.log('这是 promise3');
});
console.log('脚本结束');
执行顺序:
console.log('脚本开始')—— 同步代码,立即执行。setTimeout回调注册到宏任务队列。Promise回调注册到微任务队列。console.log('脚本结束')—— 同步代码,立即执行。- 当前宏任务执行完毕,执行微任务队列:
console.log('这是 promise1')- 注册新的微任务
console.log('这是 promise2')到微任务队列。 - 执行微任务队列:
-
console.log('这是 promise2')- 注册新的微任务
console.log('这是 promise3')到微任务队列。 - 执行微任务队列:
console.log('这是 promise3')
-
- 微任务执行完毕,执行宏任务队列:
console.log('这是 setTimeout')
输出结果:
脚本开始 脚本结束 这是 promise1 这是 promise2 这是 promise3 这是 setTimeout
总结
通过这些详细的例子,相信能够让大家对 JavaScript 的宏任务与微任务一定有了更深入的了解。这个也是我们面试时经常被问到的问题,所以希望大家好好看看,学到更多东西~感谢阅读。
以上关于终于搞懂 JavaScript 宏任务与微任务了,再也不怕面试被问到了的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » 终于搞懂 JavaScript 宏任务与微任务了,再也不怕面试被问到了
微信
支付宝