前端笔记-JS的事件循环机制

学习过程中看到了一篇很不错的博客,加深了我对JS循环机制的理解事件循环机制(Event Loop)的基本认知一、什么是事件循环机制? 为什么是上面的顺序呢? 原因是JS引擎指向代码是 - 掘金

为什么需要事件循环?

JavaScript的单线程语言,这意味着他一次只能执行一个任务,但是浏览器里面进行的事务往往是多线程的。如果没有事件循环机制,当遇到耗时操作时(网络请求、文件读取等等),整个程序会被阻塞,用户界面会被“冻结”,导致极差的用户体验。

而事件循环则通过异步非阻塞的方式解决了这个问题,让单线程的JavaScript也能高效处理并发操作。

如何更好地理解事件循环

可以看下面的这个例子:

console.log('1. 开始');

setTimeout(() => {
  console.log('6. setTimeout回调');
}, 0);

Promise.resolve().then(() => {
  console.log('4. Promise微任务1');
}).then(() => {
  console.log('5. Promise微任务2');
});

console.log('2. 中间');

new Promise(resolve => {
  console.log('3. Promise构造器');
  resolve();
}).then(() => {
  console.log('7. Promise微任务3');
});

console.log('8. 结束');

 输出结果:

1. 开始
2. 中间
3. Promise构造器
8. 结束
4. Promise微任务1
5. Promise微任务2
7. Promise微任务3
6. setTimeout回调

为什么会是这个结果呢,首先我们可以简单看一下事件循环的流程图

[调用栈] → [微任务队列] → [渲染] → [宏任务队列]
       ↖_________________________↙

遇到不同的任务代码,js会将其放到不同的队列/栈中,这里面包含了同步代码、微任务、宏任务 

同步代码

立即执行​:

  • 不在任何队列中排队
  • 遇到即执行,没有延迟

阻塞性​:

console.log('开始');
for(let i = 0; i < 1000000000; i++) {} // 同步阻塞
console.log('结束'); // 要等待循环结束才会执行

执行上下文​:

  • 构成调用栈(Call Stack)的主体
  • 每个函数调用都会创建一个新的执行上下文

微任务队列 (Microtask Queue)

触发时机​:在当前任务执行完毕后立即执行

包含内容​:

  • Promise 的 .then()/.catch()/.finally() 回调
  • queueMicrotask() 添加的任务
  • MutationObserver 回调(浏览器环境)

    特点​:

    • 优先级第二高
    • 必须清空才会进入下一阶段
    • 如果微任务中又添加微任务,会继续执行直到队列为空

    宏任务队列 (Macrotask Queue/Task Queue)

    触发时机​:每次事件循环的最后阶段

    包含内容​:

    • setTimeout/setInterval 回调
    • DOM 事件回调(点击、滚动等)
    • I/O 操作回调
    • setImmediate(Node.js 环境)
    • requestIdleCallback

      特点​:

      • 每次事件循环只执行一个宏任务(新标准下可能有变化)
      • 来源多样,可能有多个宏任务队列

      想象你在餐厅点餐:

      同步代码​:你直接向厨师点餐(立即执行)

      微任务​:厨师完成主菜后立即添加的装饰(快速完成的小任务)

      宏任务​:需要等待的甜点(稍后完成的任务)

      事件循环​:服务员不断检查是否有新订单或已完成菜品可以上桌

      厨师(JS引擎)会优先处理即时订单(同步代码),然后快速完成小装饰(微任务),最后处理需要等待的甜点(宏任务),然后又开始新一轮检查。

      JS与现实世界类比
              服务员(事件循环)只有一个,但需要服务多桌客人
              同步点餐:立即处理的简单请求(如倒水)
              微任务:快速完成的小请求(如拿餐具)
              宏任务:耗时的请求(如烹饪主菜)

      事件循环的核心优势

       1. 非阻塞I/O处理

      // 没有事件循环的"阻塞"写法(伪代码)
      const data = readFileSync('largeFile.txt'); // 整个程序会停在这里等待
      console.log(data);
      
      // 有事件循环的实际写法
      readFile('largeFile.txt', (err, data) => {
        console.log(data); // 回调函数在文件读取完成后执行
      });
      console.log('我可以继续执行其他任务');

      在没有事件循环的阻塞写法中,readFileSync函数会使整个程序停在原地,等待文件读取操作完成,期间无法执行其他任何任务。而借助事件循环机制的readFile函数,采用异步回调的方式,在文件读取的过程中,程序能够继续执行后续的console.log('我可以继续执行其他任务');语句,实现了非阻塞 I/O 处理,大大提高了程序的执行效率和资源利用率。 

      2. 高响应性的用户界面

      // 按钮点击处理
      button.addEventListener('click', () => {
        // 即使这里有个耗时操作
        setTimeout(() => {
          console.log('耗时操作完成');
        }, 3000);
        // UI仍然可以响应其他操作
      });

      当用户点击按钮时,即使在点击事件处理函数中设置了一个耗时 3 秒的setTimeout操作,由于事件循环机制的存在,用户界面依然能够响应用户的其他操作,比如再次点击按钮、滚动页面等,不会因为这个耗时操作而失去响应,保证了用户界面的高响应性,提升了用户体验。

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

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

      余额充值