事件循环
JavaScript 的事件循环(Event Loop)是其异步编程模型的核心机制,它使得 JavaScript 能够在单线程上执行代码的同时处理异步操作(如网络请求、定时器等)。理解事件循环对于编写高效且无阻塞的 JavaScript 代码至关重要。以下是关于 JavaScript 事件循环的基本概念和工作流程的概述:
基本概念
调用栈(Call Stack):这是 JavaScript 执行函数的地方。每当一个函数被调用时,它就会被添加到调用栈中,并在执行完成后从栈中移除。
任务队列(Task Queue):分为宏任务(macrotask)和微任务(microtask)两种类型。宏任务包括整体代码块、
setTimeout、setInterval、I/O、UI 渲染等;微任务包括Promise的回调、MutationObserver等。事件循环:负责监控调用栈和任务队列,当调用栈为空时,事件循环会从任务队列中取出任务并推入调用栈中执行。
工作流程
首先,JavaScript 引擎开始执行全局脚本,所有同步代码都会依次进入调用栈中执行。
当遇到异步操作(比如定时器或网络请求),这些操作不会直接阻塞执行流,而是被安排在后台运行,完成后将相应的回调函数放入任务队列中。
在每个任务(无论是宏任务还是微任务)完成后,如果存在微任务,则会立即执行所有的微任务,直到微任务队列为空。
完成当前宏任务及其所有微任务后,浏览器可以进行必要的渲染操作(这并非总是发生,取决于具体的实现)。
然后,事件循环继续处理下一个宏任务,重复上述过程。
微任务 vs 宏任务
- 微任务(Microtasks):优先级较高,在当前任务完成后立即执行,通常用于需要尽快执行的操作,如
Promise的回调。 - 宏任务(Macrotasks):包括但不限于整体代码块、
setTimeout、setInterval等,它们按顺序执行,每次只取一个宏任务来执行。
了解事件循环的工作原理有助于更好地理解 JavaScript 如何管理异步操作以及如何避免常见的陷阱,例如错误地处理异步代码导致的竞态条件或未预期的行为。通过合理使用Promise、async/await等特性,可以写出更加清晰和高效的异步代码。