1.前言
任务可以分成两种,一种是同步任务(synchronous),另一种是异步(asynchronous),异步任务又分为宏任务和微任务。
同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务:不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行
console.log("这是开始");
function fn1() {
console.log("这是一条消息2");
fn2();
}
function fn2() {
console.log("这是一条消息3");
}
setTimeout(function cb1() {
console.log("这是来自第一个回调的消息");
});
console.log("这是一条消息1");
fn1();
setTimeout(function cb2() {
console.log("这是来自第二个回调的消息");
}, 0);
console.log("这是结束");
//输出的结果:
//这是开始
//这是一条消息1
//这是一条消息2
//这是一条消息3
//这是结束
//这是来自第一个回调的消息
//这是来自第二个回调的消息
2.什么是微任务?什么是宏任务?分别都有哪些?
在 JavaScript 中,宏任务和微任务是用来管理异步操作的概念。
宏任务代表一组要在主线程中执行的 JavaScript 代码。它们通常包括以下几种情况:
- 整体代码script:由多行代码组成的完整,可执行的程序代码,如
<script>js代码</script>
- setTimeout 和 setInterval:通过定时器触发的代码。
- I/O 操作:包括读取文件、发送网络请求等异步操作。
- UI 渲染:DOM渲染,即更改代码重新渲染DOM的过程。
- requestAnimationFrame:在下一次页面重绘之前执行的操作。
宏任务之间的执行顺序:setTimeout --> setInterval --> i/o操作 --> 异步ajax
微任务是一组需要在当前任务执行完成后尽快执行的 JavaScript 代码。它们在事件循环的当前阶段结束后立即执行,而不是等待下一个宏任务执行。常见的微任务包括:
- Promise 回调(then,catch,finally):Promise 的处理函数会作为微任务执行。
- MutationObserver:用于监听 DOM 变化的 API。
3.Object.observe:用来实时监听js中对象的变化 - process.nextTick(Node.js 环境下):将回调函数放入微任务队列中。
微任务之间的执行顺序:process.nextTick --> Promise
当 JavaScript 引擎执行代码时,它会先执行当前的宏任务,然后检查微任务队列是否为空。如果微任务队列不为空,引擎会依次执行微任务,直到微任务队列为空。接着,引擎会继续执行下一个宏任务。这个过程不断循环,形成了事件循环(event loop),如下图所示。
理解宏任务和微任务的执行顺序对于理解 JavaScript 异步编程很重要。宏任务通常会在一个事件循环的阶段结束后执行,而微任务则会在当前任务执行完成后立即执行。这就意味着微任务可以在宏任务之前执行,因此它们具有更高的优先级。
3.案例
案例一
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log("timerStart");
resolve("success");
console.log("timerEnd");
}, 0);
console.log(2);
});
promise.then((res) => {
console.log(res);
});
console.log(4);
输入结果:
1
2
4
timerStart
timerEnd
success
结果分析:
- 首先遇到Promise构造函数,会先执行里面的内容,打印 1 ;
- 遇到定时器 steTimeout ,它是一个宏任务,放入宏任务队列;
- 继续向下执行,打印出2;
- 由于 Promise 的状态此时还是 pending ,所以 promise.then 先不执行;
- 继续执行下面的同步任务,打印出4;
- 此时微任务队列没有任务,继续执行下一轮宏任务,执行 steTimeout ;
- 首先执行 timerStart ,然后遇到了 resolve ,将 promise 的状态改为 resolved 且保存结果并将
- 之前的 promise.then 推入微任务队列,再执行 timerEnd ;
- 执行完这个宏任务,就去执行微任务 promise.then ,打印出 resolve 的结果。
案例2
Promise.resolve().then(() => {
console.log('promise1');
const timer2 = setTimeout(() => {
console.log('timer2')
}, 0)
});
const timer1 = setTimeout(() => {
console.log('timer1')
Promise.resolve().then(() => {
console.log('promise2')
})
}, 0)
console.log('start');
输出结果:
start
promise1
timer1
promise2
timer2
结果分析:
- 首先, Promise.resolve().then 是一个微任务,加入微任务队列
- 执行timer1,它是一个宏任务,加入宏任务队列
- 继续执行下面的同步代码,打印出 start
- 这样第一轮宏任务就执行完了,开始执行微任务 Promise.resolve().then ,打印出 promise1
- 遇到 timer2 ,它是一个宏任务,将其加入宏任务队列,此时宏任务队列有两个任务,分别是
timer1 、 timer2 ; - 这样第一轮微任务就执行完了,开始执行第二轮宏任务,首先执行定时器 timer1 ,打印
timer1 ; - 遇到 Promise.resolve().then ,它是一个微任务,加入微任务队列
- 开始执行微任务队列中的任务,打印 promise2 ;
- 最后执行宏任务 timer2 定时器,打印出 timer2 ;