JavaScript 中的异步任务、同步任务、宏任务与微任务

JavaScript 中的异步任务、同步任务、宏任务与微任务

在 JavaScript 的世界里,理解异步任务、同步任务、宏任务和微任务是非常重要的,它们共同构成了 JavaScript 独特的执行机制。

一、同步任务与异步任务

1. 同步任务

  • 定义:同步任务是在代码执行过程中,按照顺序依次执行的任务。每个同步任务必须等待前一个任务完成后才能开始执行。
  • 特点
    • 阻塞代码执行,直到任务完成。
    • 按照代码书写的顺序依次执行。
  • 示例
    console.log('同步任务 1');
    console.log('同步任务 2');
    
    在这个例子中,首先会输出“同步任务 1”,然后输出“同步任务 2”。这两个任务是按照顺序依次执行的,前一个任务完成后,后一个任务才会开始执行。

2. 异步任务

  • 定义:异步任务是在代码执行过程中,不会立即执行,而是在特定的时间或条件满足后才会执行的任务。异步任务不会阻塞代码的执行,允许其他任务在等待异步任务完成的过程中继续执行。
  • 特点
    • 不会阻塞代码执行,可以在后台执行。
    • 通常需要回调函数来处理结果。
  • 示例
    console.log('同步任务 1');
    setTimeout(() => {
        console.log('异步任务');
    }, 1000);
    console.log('同步任务 2');
    
    在这个例子中,首先会输出“同步任务 1”,然后输出“同步任务 2”。接着,由于setTimeout是一个异步任务,它会在 1000 毫秒后执行回调函数,输出“异步任务”。在等待异步任务执行的过程中,其他同步任务可以继续执行。

二、宏任务与微任务

1. 宏任务

  • 定义:宏任务是由浏览器或 Node.js 等环境提供的任务,通常包括setTimeoutsetIntervalAjax 请求DOM 事件等。宏任务会在主线程上依次执行,每个宏任务执行完毕后,会检查微任务队列是否为空,如果不为空,则执行微任务队列中的所有任务。
  • 特点
    • 执行时间相对较长。
    • 可能会导致页面的重新渲染。
  • 示例
    console.log('同步任务 1');
    setTimeout(() => {
        console.log('宏任务 1');
    }, 0);
    console.log('同步任务 2');
    
    在这个例子中,setTimeout中的回调函数是一个宏任务,会在同步任务执行完毕后被放入任务队列等待执行。即使设置的时间为 0,也不会立即执行,而是在同步任务执行完毕后,按照任务队列的顺序执行。

2. 微任务

  • 定义:微任务是在当前宏任务执行过程中产生的,并且会在当前宏任务执行完毕后立即执行。常见的微任务包括Promise.then()Promise.catch()Promise.finally()MutationObserver等。
  • 特点
    • 执行时间相对较短。
    • 优先级高于宏任务。
  • 示例
    console.log('同步任务 1');
    Promise.resolve().then(() => {
        console.log('微任务 1');
    });
    console.log('同步任务 2');
    
    在这个例子中,Promise.resolve().then()中的回调函数是一个微任务,会在同步任务执行完毕后,且在宏任务执行之前被执行。

三、事件循环

JavaScript 是单线程语言,通过事件循环来管理同步任务和异步任务的执行。事件循环的工作原理如下:

  1. 首先执行同步任务,将同步任务依次放入主线程执行。
  2. 当遇到异步任务时,将异步任务放入任务队列中等待执行。异步任务分为宏任务和微任务,分别放入不同的任务队列。
  3. 当主线程中的同步任务执行完毕后,会先检查微任务队列是否为空。如果微任务队列不为空,则执行微任务队列中的所有任务,这个过程会持续进行,直到微任务队列为空。
  4. 微任务队列处理完后,才会从宏任务队列中取出一个宏任务并执行,宏任务执行产生微任务会执行微任务队列的任务。
  5. 重复步骤 3 和 4,直到任务队列中的所有任务都被执行完毕。

例如:

console.log('同步任务 1');
setTimeout(() => {
    console.log('宏任务 1');
}, 0);
Promise.resolve().then(() => {
    console.log('微任务 1');
});
console.log('同步任务 2');

在这个例子中,首先执行“同步任务 1”和“同步任务 2”。然后,由于setTimeout是宏任务,它会被放入宏任务队列中等待执行。同时,Promise.resolve().then()是微任务,会被放入微任务队列中。当同步任务执行完毕后,会先执行微任务队列中的“微任务 1”,然后才会从宏任务队列中取出“宏任务 1”执行。

面试题:

console.log('同步任务 start');

setTimeout(() => {
    console.log('宏任务 1');
    Promise.resolve().then(() => {
        console.log('微任务 within 宏任务 1');
    });
}, 0);

Promise.resolve().then(() => {
    console.log('微任务 1');
    setTimeout(() => {
        console.log('宏任务 within 微任务 1');
    }, 0);
});

setTimeout(() => {
    console.log('宏任务 2');
}, 0);

console.log('同步任务 end');

输出结果:

> "同步任务 start"
> "同步任务 end"
> "微任务 1"
> "宏任务 1"
> "微任务 within 宏任务 1"
> "宏任务 2"
> "宏任务 within 微任务 1"

以下是对上述代码执行流程的解释:

  1. 首先,执行同步任务。
    • 输出同步任务 start
    • 接着遇到第二个同步任务,输出同步任务 end
  2. 同步任务执行完毕后,开始检查微任务队列。
    • 此时微任务队列为空,所以继续从宏任务队列中取出任务执行。
  3. 从宏任务队列中取出第一个由setTimeout注册的宏任务执行。
    • 输出宏任务 1
    • 在这个宏任务中,又有一个Promise.resolve().then(),它会注册一个微任务,即console.log('微任务 within 宏任务 1');被放入微任务队列。
  4. 接着,回到宏任务队列继续检查是否还有未执行的宏任务。
    • 由于还有两个由setTimeout注册的宏任务未执行,但是根据事件循环机制,此时要先检查微任务队列。
  5. 微任务队列中有一个任务,即console.log('微任务 within 宏任务 1');,执行这个微任务,输出微任务 within 宏任务 1
  6. 微任务队列处理完毕后,再次从宏任务队列中取出下一个任务执行。
    • 输出宏任务 2
  7. 此时宏任务队列中还有一个任务,是在微任务 1中注册的setTimeout回调,即console.log('宏任务 within 微任务 1');
  8. 执行这个宏任务,输出宏任务 within 微任务 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

John Rivers

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值