likes
comments
collection
share

Event Loop:都给我乖乖站好排队

作者站长头像
站长
· 阅读数 24

疑惑

为什么会要有Event Loop这种执行机制?仅依靠同步执行代码不能够完成执行任务吗?

接下来用一个例子展示出它存在于代码执行机制中的必要性:

<script>
        console.log(1111);
        // 宏任务
        setTimeout(()=>{
            console.log('1234');
        },3000)
        console.log("22222");
        // 宏任务
        setTimeout(()=>{
            console.log('5678');
        },5000)
     </script>

由于js是一种单线程语言,任何给定时刻只能执行一个任务 由此理解,这段代码的输出会是按顺序输出吗?如果是以同步代码同步执行的理解,输出顺序会是

11111234222225678

但是如果这样的话,宏任务的出现会使后续代码阻塞。

为什么会需要Event Loop

但是如果在真正的项目中,也是以同步执行的机制,所有的代码都要等待前面代码执行完之后才能执行时,程序的灵活性和效率将会大大降低,因为项目中经常会有一些宏任务(异步任务的一种),也就是需要一定时间才能够完成的任务,在同步执行的机制下,所有代码需要等待前方的宏任务执行完毕,这样会造成代码的阻塞,大大降低程序执行的效率和用户的体验。所以为了解决这个问题,Event Loop机制横空出世,以上代码在Event Loop的执行机制下,输出顺序会是:

11112222212345678

原因 是因为在js的event loop执行机制中,会首先执行同步代码console.log(1111),在执行之后准备执行setTimeout(()=>{ console.log('1234'); },3000)这段代码时,v8检测到这段代码是宏任务,也就是需要一些时间去处理,所以它不会立即执行回调函数,而是将一个定时器事件在事件队列中注册,三秒后再将回调函数从事件队列中取出在执行栈中执行,下面的setTimeout(()=>{ console.log('5678'); },5000)此代码也是同理。

Event Loop底层过程

事件循环主要由以下几个部分组成:

  1. 调用堆栈(Call Stack) :这是一个后进先出的数据结构,用于存储正在执行的函数。每当一个新的函数被调用时,它就会被压入调用堆栈;当函数执行完毕,它就会从调用堆栈中弹出。
  2. 消息队列(Message Queue) :这是一个先进先出的数据结构,用于存储待处理的异步回调函数。每当一个异步操作完成时,相应的回调函数就会被放入消息队列。
  3. 事件循环(Event Loop) :这是整个机制的心脏,它不断地检查调用堆栈和消息队列。当调用堆栈为空时,事件循环会从消息队列中取出一个任务,将其压入调用堆栈,开始执行。

工作流程

  1. 执行同步代码:JavaScript引擎执行代码中的同步部分,将每个函数压入调用堆栈,执行完毕后弹出。
  2. 处理异步操作:遇到异步操作(如setTimeoutfetchsetInterval等)时,JavaScript引擎不会等待这些操作完成,而是立即返回控制权给调用堆栈顶部的函数,同时将异步操作的回调函数放入事件队列中等待。
  3. 事件循环检查:当调用堆栈为空时,事件循环会检查消息队列。如果消息队列中有任务,它会取出队列中的第一个任务,压入调用堆栈执行。
  4. 重复上述过程:事件循环会持续检查调用堆栈和消息队列,直到消息队列为空且没有更多的异步操作待处理。

小结

总的来说,在传统的同步编程中,程序会按顺序执行每一行代码,直到所有任务完成。但在异步编程中,某些任务可能需要等待(例如,从网络请求数据),而程序不应该在这期间停止运行。这就需要用到Event Loop,通过这种方式允许程序在等待某些操作的同时继续执行其他任务,从而实现非阻塞和高效的资源利用。在JavaScript中,这种机制使得即使在处理耗时的I/O操作时,Web应用程序也能保持响应性。

转载自:https://juejin.cn/post/7387269247963152418
评论
请登录