Js中的微任务和宏任务

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
//这是结束
//这是来自第一个回调的消息
//这是来自第二个回调的消息

Js中的微任务和宏任务_第1张图片

2.什么是微任务?什么是宏任务?分别都有哪些?

在 JavaScript 中,宏任务和微任务是用来管理异步操作的概念。

宏任务代表一组要在主线程中执行的 JavaScript 代码。它们通常包括以下几种情况:

  1. 整体代码script:由多行代码组成的完整,可执行的程序代码,如
  2. setTimeout 和 setInterval:通过定时器触发的代码。
  3. I/O 操作:包括读取文件、发送网络请求等异步操作。
  4. UI 渲染:DOM渲染,即更改代码重新渲染DOM的过程。
  5. requestAnimationFrame:在下一次页面重绘之前执行的操作。

宏任务之间的执行顺序:setTimeout --> setInterval --> i/o操作 --> 异步ajax

微任务是一组需要在当前任务执行完成后尽快执行的 JavaScript 代码。它们在事件循环的当前阶段结束后立即执行,而不是等待下一个宏任务执行。常见的微任务包括:

  1. Promise 回调(then,catch,finally):Promise 的处理函数会作为微任务执行。
  2. MutationObserver:用于监听 DOM 变化的 API。
    3.Object.observe:用来实时监听js中对象的变化
  3. process.nextTick(Node.js 环境下):将回调函数放入微任务队列中。

微任务之间的执行顺序:process.nextTick --> Promise

当 JavaScript 引擎执行代码时,它会先执行当前的宏任务,然后检查微任务队列是否为空。如果微任务队列不为空,引擎会依次执行微任务,直到微任务队列为空。接着,引擎会继续执行下一个宏任务。这个过程不断循环,形成了事件循环(event loop),如下图所示。
Js中的微任务和宏任务_第2张图片

理解宏任务和微任务的执行顺序对于理解 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

结果分析:

  1. 首先遇到Promise构造函数,会先执行里面的内容,打印 1 ;
  2. 遇到定时器 steTimeout ,它是一个宏任务,放入宏任务队列;
  3. 继续向下执行,打印出2;
  4. 由于 Promise 的状态此时还是 pending ,所以 promise.then 先不执行;
  5. 继续执行下面的同步任务,打印出4;
  6. 此时微任务队列没有任务,继续执行下一轮宏任务,执行 steTimeout ;
  7. 首先执行 timerStart ,然后遇到了 resolve ,将 promise 的状态改为 resolved 且保存结果并将
  8. 之前的 promise.then 推入微任务队列,再执行 timerEnd ;
  9. 执行完这个宏任务,就去执行微任务 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

结果分析:

  1. 首先, Promise.resolve().then 是一个微任务,加入微任务队列
  2. 执行timer1,它是一个宏任务,加入宏任务队列
  3. 继续执行下面的同步代码,打印出 start
  4. 这样第一轮宏任务就执行完了,开始执行微任务 Promise.resolve().then ,打印出 promise1
  5. 遇到 timer2 ,它是一个宏任务,将其加入宏任务队列,此时宏任务队列有两个任务,分别是
    timer1 、 timer2 ;
  6. 这样第一轮微任务就执行完了,开始执行第二轮宏任务,首先执行定时器 timer1 ,打印
    timer1 ;
  7. 遇到 Promise.resolve().then ,它是一个微任务,加入微任务队列
  8. 开始执行微任务队列中的任务,打印 promise2 ;
  9. 最后执行宏任务 timer2 定时器,打印出 timer2 ;

你可能感兴趣的:(javascript,前端)