如何理解JS的事件循环机制?

你好同学,我是沐爸,欢迎点赞、收藏、评论和关注!

JavaScript 的事件循环(Event Loop)机制是 JavaScript 运行时环境(如浏览器和Node.js)处理异步操作的一种核心机制。这个机制允许 JavaScript 在单线程环境中执行代码,同时能够处理诸如网络请求、定时器、用户交互等异步事件。下面将详细讲解 JavaScript 的事件循环机制。

image.png

一、基本概念

1.单线程:JavaScript 语言的一大特点就是单线程,这意味着JavaScript 在同一时间只能执行一个任务。
2.任务队列:JavaScript 运行时环境维护了一个或多个任务队列,用于存放异步任务的回调函数,任务按类型分为宏任务和微任务。
3.调用栈(Call Stack):JavaScript 引擎使用调用栈来管理函数的执行。当函数被调用时,它会被推入调用栈,并在执行完毕后从栈中弹出。

二、任务类型

任务类型分为同步任务和异步任务。异步任务又分为宏任务和微任务。宏任务由浏览器或 node 发起,微任务时由 JS 引擎发起的任务。微任务的优先级高于宏任务
image.png

三、事件循环执行流程

1.执行同步代码
JS 引擎执行 JS 代码时,会在主进程生成调用栈。首先会执行所有同步代码,同步函数先入栈,再出栈。
2.处理异步操作
当遇到异步操作时(如setTimeout、Promise等),JS 引擎会将异步操作交给 WebApi 处理,并继续执行调用栈中的其他同步代码。WebApi 会将异步任务的回调函数推入任务队列中等待执行。
3.执行微任务
所有同步任务执行后,调用栈为空时,JS 引擎开始查询异步任务队列,先检查微任务队列是否有任务要执行。如果有,就会依次执行微任务队列中的所有任务,直到微任务队列为空。如果没有,则去查询宏任务队列。
4.执行宏任务
当微任务队列清空后,JavaScript 引擎会去查询宏任务队列,如果有任务,则从任务队列中取出一个宏任务执行(先进先出原则)。执行完毕后,再次检查微任务队列,重复上述过程,直至微任务和宏任务队列都为空。
5.特殊情况
如果微任务中有宏任务,或者宏任务中有微任务时,JS 引擎会将新的异步操作交给 WebApi 处理,并继续执行回调函数中的其他代码。
一个微任务执行完毕后,会继续执行下一个微任务,如果有的话。一个宏任务执行完毕后,不会立即执行下一个宏任务,而是先检查是否有新的微任务,如果有先执行微任务,如果没有则执行下一个宏任务。

四、示例

以下是一个简单的示例,展示了事件循环的执行顺序:

console.log('1');

// setTimeout-1
setTimeout(() => {
    console.log('2');
}, 0);

// Promise-1
Promise.resolve().then(() => {
    console.log('3');
});

// setTimeout-2
setTimeout(() => {
    console.log('4');
    // Promise-2
    Promise.resolve().then(() => {
        console.log('5');
    });
}, 0);

// Promise-3
Promise.resolve().then(() => {
    console.log('6');

    // setTimeout-3
    setTimeout(() => {
        console.log('7');
    }, 0);
});

console.log('8');
  
// 输出结果:1 8 3 6 2 4 5 7

代码解析:
第一步,首先执行同步代码,输出了1和8。
第二步,将微任务 Promise 的回调函数放入微任务队列中,队列列表:[“Promise-1”, “Promise-3”],将宏任务 setTimeout 的回调函数放入宏任务队列中,队列列表:[“setTimeout-1”, “setTimeout-2”]
第三步,读取微任务队列,依次取出"Promise-1"、“Promise-3”,输出3和6,“Promise-3"中出现新的宏任务 “setTimeout-3”,将其推到宏任务队列,此时宏任务列表为 [“setTimeout-1”, “setTimeout-2”,“setTimeout-3”]。
第四步,读取宏任务队列,先取出"setTimeout-1”,输出2;再次取出 “setTimeout-2”,输出4,出现新的微任务,将"Promise-2"放入微任务队列;在取出 “setTimeout-3"前,发现微任务队列有任务"Promise-2”,先执行"Promise-2",输出5,微任务执行完执行宏任务 “setTimeout-3”,输出7。

五、实践应用

  1. 避免阻塞主线程:长时间运行的同步代码会阻塞主线程,影响用户体验。可以通过将任务拆分为多个小任务,并使用setTimeout或requestAnimationFrame将它们分散到事件循环的多个迭代中执行。
  2. 正确处理异步操作:使用Promise和async/await可以简化异步操作的处理,避免回调地狱。async/await使得异步代码看起来更像同步代码,易于理解和维护。

好了,分享结束,谢谢点赞,下期再见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

沐爸muba

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

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

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

打赏作者

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

抵扣说明:

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

余额充值