JavaScript 的 事件循环(Event Loop) 是其执行机制的核心,用来处理异步操作,使得 JavaScript 能够实现非阻塞式的单线程异步编程。为了理解事件循环,首先要了解 JavaScript 是单线程的语言,这意味着它一次只能执行一个任务。但在实际应用中,比如 I/O 操作(网络请求、定时器、用户事件等),JavaScript 使用事件循环机制来管理异步任务,使其在需要的时候能继续执行而不阻塞主线程。
1. JavaScript 执行模型概览
JavaScript 的执行环境中包括以下几个关键概念:
-
调用栈(Call Stack):调用栈是 JavaScript 用来管理函数执行的地方。当一个函数被调用时,它会被压入调用栈,执行完成后弹出。
-
任务队列(Task Queue):也叫 消息队列(Message Queue),存放异步任务的回调函数,如
setTimeout
、Promise
、事件处理函数等。 -
事件循环(Event Loop):事件循环是负责检查调用栈是否为空,并决定何时将任务队列中的任务推入调用栈执行的机制。
2. 事件循环的运行机制
事件循环的核心工作流程如下:
-
同步任务:所有同步任务都会直接在 调用栈(Call Stack) 上依次执行。
-
异步任务:当遇到异步任务(如
setTimeout
、Promise
、I/O 操作)时,这些任务会被放到相应的 任务队列 中,而不会立即执行。 -
事件循环(Event Loop):事件循环会不断检查调用栈是否为空。当调用栈为空时,它会查看 任务队列 是否有可执行的任务。如果有,它会将任务队列中的第一个任务放入调用栈中执行。
-
继续执行:当某个异步任务的回调函数被执行完,事件循环会再次检查任务队列,重复上述过程。
3. 宏任务和微任务
JavaScript 中的异步任务大致可以分为 宏任务(Macro-task) 和 微任务(Micro-task),这两者的执行顺序是事件循环中至关重要的部分。
-
宏任务(Macro-task)包括:
setTimeout
、setInterval
、I/O
操作、script
(整体代码)、事件监听器等。 -
微任务(Micro-task)包括:
Promise.then()
、process.nextTick
(Node.js)、MutationObserver
。
执行顺序:
-
事件循环每次循环称为一个 tick,每次 tick 开始时,会先执行 调用栈中的所有同步任务。
-
当调用栈为空后,会执行 微任务队列(Micro-task Queue) 中的所有任务,直到微任务队列清空。
-
在微任务执行完毕之后,事件循环才会去执行下一个 宏任务队列 中的任务。
-
重复以上步骤。
示例:
console.log('Start'); // 同步任务
setTimeout(() => {
console.log('Macro-task: setTimeout'); // 宏任务
}, 0);
Promise.resolve().then(() => {
console.log('Micro-task: Promise'); // 微任务
});
console.log('End'); // 同步任务
执行顺序:
console.log('Start')
和console.log('End')
是同步任务,直接进入调用栈依次执行。setTimeout
是宏任务,它的回调函数会被放入宏任务队列中,稍后执行。Promise.then
是微任务,微任务会在同步任务结束后立即执行。- 最后,
setTimeout
的回调才会执行。
输出结果:
Start
End
Micro-task: Promise
Macro-task: setTimeout
4. 典型的异步操作
-
setTimeout
和setInterval
:它们是常见的定时器函数,回调函数会被放入宏任务队列中,等待事件循环调度。 -
Promise
:Promise
是微任务,它的then
和catch
回调会被放入微任务队列中,优先于宏任务执行。 -
requestAnimationFrame
:这个函数专门用于高效的动画渲染,在浏览器的帧刷新之前执行其回调。 -
I/O 操作:如网络请求的回调会在 I/O 事件完成后放入宏任务队列中。
5. 事件循环在浏览器和 Node.js 中的区别
-
浏览器 中,事件循环的机制更侧重于处理用户事件和 UI 渲染。每次事件循环中的宏任务执行完之后,浏览器会重新渲染页面,然后再执行下一个任务。
-
Node.js 的事件循环更复杂一些,尤其在处理 I/O 和定时器方面有更详细的阶段划分。Node.js 的事件循环大致分为几个阶段:
timers
阶段(执行定时器回调)、I/O callbacks
阶段、idle, prepare
阶段、poll
阶段(处理 I/O 事件)、check
阶段(执行setImmediate
)、close
阶段(关闭回调)。
6. 事件循环的应用场景
-
异步编程:事件循环机制使得 JavaScript 能够在单线程中高效处理 I/O 操作、定时器、DOM 操作等任务,不会阻塞主线程。
-
优化性能:在用户界面更新时,长时间的同步任务可能导致页面卡顿,理解事件循环可以帮助你将耗时的任务异步处理,从而保持页面的流畅度。
-
微任务与宏任务优化:通过合理使用微任务和宏任务,你可以控制任务的优先级,从而优化程序的执行顺序。
总结:
- 事件循环 是 JavaScript 中实现异步编程的核心机制,它不断检查调用栈和任务队列,确保异步任务在适当的时间点执行。
- 异步任务分为 宏任务 和 微任务,微任务的优先级高于宏任务。
- 事件循环的机制使得 JavaScript 能够处理大量 I/O、定时器、DOM 操作等任务而不阻塞主线程执行。
理解事件循环对于编写高效、流畅的 JavaScript 代码至关重要。
标签:异步,JavaScript,javascript,任务,循环,事件,执行 From: https://blog.csdn.net/misstianyun/article/details/142654238