JavaScript的时间循环机制(event loop)

185 阅读5分钟

我们可以把JavaScript的运行环境想象成一个事件循环队列。代码执行的时候会先进入主线程执行,遇到异步事件的时候,会把这个事件挂载到事件队列上。

举个例子:

console.log('开始'setTimeout(function(){  
  console.log('定时器执行')    
}, 0)  
console.log('结束')  

当代码执行时:

首先打印出”开始”,此时主线程开始执行
接着设置一个定时器事件,定时器是一个异步事件,会被挂载到事件队列上
主线程继续运行打印”结束”
主线程空闲时会去检查事件队列,发现了setTimeout这个异步事件
主线程会取出这个事件并执行它里面的回调函数,打印出”定时器执行”
Main线程继续监听事件队列,如果没有了就空等,有的话就取出执行,这样一个循环反复运行就是事件循环机制
所以事件循环就是一个监听者的角色,帮助JavaScript实现异步编程,区分同步和异步的执行顺序。

这个就是事件循环(Event Loop)最基本的机制和原理了。如果这个例子你还不太明白,我可以想其它生活中的通俗比喻再举例子讲解。你有哪里不清楚吗?

JavaScript的事件循环机制:

我们可以把JavaScript运行环境想象成一间忙碌的餐厅,主线程就是那位非常忙碌的主厨。

客人的订单进来了(console.log('开始')),主厨接收到后,立刻开始做料理,这个过程就是主线程运行同步代码。
主厨一边做饭,一边接到一个电话预定(setTimeout),要求一会儿后送一份外卖到门店。主厨把这张预定单贴到墙上(事件队列),继续忙自己手头的活。这就是异步代码被挂起,放入事件队列的过程。
主厨终于完成了客人的订单(console.log('结束')),这时候主线程的调用栈为空闲了下来。
主厨开始检查墙上的预定清单(事件队列),发现有人预定外卖,然后开始做外卖的料理(回调函数)。这就是事件循环机制,主线程从事件队列中取出异步事件执行回调函数。
主厨将做好的外卖打包妥当,派送出去后,继续检查墙上的清单,重复之前的操作......
在餐厅这个环境中,主厨就好像JavaScript主线程,接订单、炒菜就是在执行主线程代码;而墙上的预定清单就是事件队列,主厨空闲下来的时候会检查墙上的单子,取下来执行。

另一种说法

首先,JavaScript的一大特点是单线程,也就是同一时间只能做一件事。那么JavaScript引擎在单线程里面如何能够实现异步编程呢?这就需要事件循环机制来协调操作。

事件循环机制实际包含两个非常重要的部分:

主线程:负责执行代码,是一个while(true)的循环,每次循环就是一个"tick"
任务队列:一个先进先出的队列,存放各种需要异步执行的任务
当代码执行的时候:

所有同步任务直接在主线程上执行,形成一个执行栈。
异步函数比如定时器或者网络请求,会先记录下来,然后下次循环再检查是否到了执行时机。
主线程内的任务执行完毕为空,会从任务队列中取出第一个任务,推入执行栈,执行完后返回第一步。
不断反复这个操作,就形成了一个无限的循环,这就是事件循环(Event Loop)机制。
事件循环也解决了JavaScript异步编程中"先执行者优先"的问题,让主线程与任务队列里的任务有序合理的执行。

这样,利用单线程+事件循环+任务队列的机制,JavaScript实现了异步编程,这就是事件循环机制的全貌。

结合阅读哈()

JavaScript语言采用的是单线程程序设计模型,也就是代码都在主线程上以运行到完成。但是很多场景下又需要异步处理比如网络请求、定时任务等。这时就需要事件循环机制来协调主线程与任务队列的执行流程。

主要包含两个要点:

1. 事件队列(Event Queue)

事件队列是一个FIFO(先进先出)的队列,用于存放异步任务。常见的事件队列包括:

Macrotask Queue: 用于存放setTimeout、setInterval等宏任务
Microtask Queue:用于存放Promise回调等微任务
DOM事件队列、网络请求回调队列等
2. 事件循环(Event Loop)

事件循环可以看作一个过程:

检查主线程Call Stack是否为空,如果为空(同步代码执行完毕),检查Microtask Queue,如果有回调则推入栈执行,直到microtask queue清空。
检查Macrotask Queue中是否有宏任务,如果有则推入主线程执行,执行完后检查Microtask queue。
不断重复上述操作步骤,这样就实现了异步任务与主线程代码的协作执行。
综上所述,JavaScript利用事件队列收集回调,利用事件循环从队列中取出回调并交给主线程执行,实现了异步编程所需的并发模型,大大优化了代码性能,