深入了解JavaScript的Event Loop 机制

在面试中,面试官如果想要考察我们对JavaScript的理解到了哪种地步,他大概率会问一句:"能介绍下JavaScript的事件循环机制吗?"或者"对宏任务和微任务有了解吗?"。

先来看一道面试题:

JavaScript 复制代码
console.log('1')

setTimeout(function callback(){
	console.log('2')
}, 1000)

new Promise((resolve, reject) => {
    console.log('3')
    resolve()
})
.then(res => {
    console.log('4');
})

console.log('5')

它的输出结果不难看出:1、3、5、4、2。

这三种不同的行为实际上是考察我们对时间循环机制的认识。接下来我们将围绕这道题来认识时间循环机制。

一、JavaScript是单线程的

JavaScript是一门单线程的编程语言,这也就意味着同一时间内只能做一件事,所有的任务必须按顺序进行。

那为什么JavaScript就只能是单线程的,为什么不能是多线程的?

因为JavaScript是浏览器脚本语言,主要用途是和用户进行互动,以及操作DOM。这就从根本上决定它只能是单线程的。试想一下,如果JavaScript有两个线程,一个是添加某个节点,而另一个是删除该节点,那浏览器这时该以哪个线程优先。很明显,这带来严重的同步问题。

二、同步任务和异步任务

单线程就意味着任务需要排队,只有前一个任务完成后,才会执行下一个任务。如果前一个任务耗时很长,下一个任务就不得不一直等待。

我们再来看下上面那段代码:

JavaScript 复制代码
console.log('1')

setTimeout(function (){
	console.log('2')
}, 1000)

console.log('3')

/* 运行结果:
	1
	3
	2
*/

看到这有些人不禁感到疑惑,JavaScript不是单线程的吗,不应该是从上往下依次运行的吗,结果不应该是1、2、3吗,怎么会是1、3、2呢?

JavaScript的开发人员也意识到了这个问题,所以为了不影响主线程的正常运行,会把耗时性的任务依次放进一个任务队列中,等到主线程任务执行完后,才会回过头来执行队列中的任务。

这样,JavaScript中的任务就分为两种:

  • 同步任务:在主线程上,按顺序依次执行的任务。
  • 异步任务:被添加到"任务队列"中的任务,只有"任务队列"通知主线程某个异步任务可以执行后,才会把该异步任务添加到主线程中。

三、事件循环(Event Loop)

时间循环机制是JavaScript的核心运行机制之一,其中涉及几个关键概念:调用栈、任务队列、微任务和宏任务。

那它是如何执行的呢:

  1. 所有同步任务形成一个调用栈,在主线程上执行。
  2. 除了主线程,还有一个任务队列,为浏览器中的异步代码开辟了一个单独的空间。当异步代码执行完,就会被送回任务队列中(遵循先进先出的原则)。
  3. 一旦主线程的同步任务执行完后,调用栈为空,系统就会将队列中的回调函数依次压入调用栈中。当调用栈为空,就会不断检查任务队列中是否有代码需要执行。

运行如图所示:

这下,就能明白为什么输出结果会是1、3、2了吧。

四、宏任务和微任务

重新回到最初的面试题,它的结果是1、3、5、4、2。其中1、3、5的输出原因我们已经了解过了。setTimeout()和Promise()都是异步函数,而且setTimeout()还在Promise()之前,为什么会先打印4,而不是2呢?

这就涉及另一个知识点了。其实异步任务也是有区别的:分为宏任务(macrotask)和微任务(microtask)。

其中宏任务有setTimeoutsetIntervalUI rendering等,微任务promiserequestAnimationFrame等。

既然异步任务都分成宏任务和微任务,自然就有宏任务队列和微任务队列。

当宏任务和微任务同时在任务队列中,微任务的优先级高于宏任务。所以会先执行微任务,再执行宏任务。

现在,整个事件循环机制的执行过程就很明了了:

  1. 执行全局同步任务代码。
  2. 执行微任务队列中的所有任务。
  3. 进行DOM渲染。
  4. 执行宏任务。
  5. 重复以上步骤。

现在就不能明白,为什么打印的结果会是:1、3、5、4、2。

相关推荐
必须会一定会40 分钟前
用纯 HTML/JS 做一个 AI 需求澄清器:把模糊想法转换成可执行任务书
开发语言·前端·javascript·人工智能·html·ai编程
小帅不太帅1 小时前
1.5M 参数的 OCR 模型,我把它跑进了浏览器
前端·javascript·ai编程
avi911113 小时前
[AI教做人]AI平台做2项目;一个3D模型展示,另一个框架多人
javascript·人工智能·ai·3d模型·3d引擎·顶点和法线
kyriewen14 小时前
我用Claude Code两天干完了团队两周的排期——周报发出去那一刻我就后悔了
前端·javascript·ai编程
用户9385156350715 小时前
手写一个 LLM Harness 框架:用工程化手段把大模型幻觉踩在脚下
javascript·人工智能·后端
油丶酸萝卜别吃15 小时前
jquery-ajax.js 说明文档
前端·javascript·jquery
windliang15 小时前
Claude Code 源码分析(九):子 Agent 如何分叉、继续与回到父会话
前端·javascript·面试
柒和远方16 小时前
V063: TS 面试必考:interface 与 type 的四大差异,与 LLM Harness 的自动化择优
前端·javascript
汉堡大王952716 小时前
Vue 3 + TS + Element Plus 实战:如何从零搭建企业级违章记录管理 SaaS 前端
前端·javascript·vue.js
海天鹰16 小时前
屏幕颜色检测
javascript·html5