JavaScript通过事件循环实现异步非阻塞,先执行同步任务,再按宏任务与微任务顺序处理异步操作,微任务优先于宏任务执行。

JavaScript 是单线程语言,但它通过事件循环(Event Loop)和异步机制实现了非阻塞操作。理解 Event Loop 是掌握 JavaScript 异步编程的关键。
JavaScript 单线程与任务队列
由于 JavaScript 是单线程执行,同一时间只能处理一个任务。为了不阻塞主线程,所有耗时操作(如网络请求、定时器、DOM 事件)都被设计为异步执行。
当代码运行时,任务被分为两类:
- 同步任务:直接在主线程上执行,比如变量赋值、函数调用。
- 异步任务:不会立即执行,而是放入任务队列中等待调度。
这些异步任务又进一步分为宏任务(macrotask)和微任务(microtask),它们的执行顺序由事件循环决定。
立即学习“Java免费学习笔记(深入)”;
宏任务与微任务的区别
事件循环每次从宏任务队列中取出一个任务执行,执行完毕后,会清空当前所有的微任务,然后再进入下一轮循环。
常见的任务类型包括:
- 宏任务:setTimeout、setInterval、I/O、UI 渲染、script 标签中的整体代码。
- 微任务:Promise.then/catch/finally、MutationObserver、queueMicrotask。
举个例子帮助理解执行顺序:
输出结果是:
startend
promise
timeout
原因在于:setTimeout 是宏任务,进入宏任务队列;Promise 的回调是微任务,在当前宏任务结束后立即执行;而 script 本身是一个宏任务,执行完后先清空微任务队列,再进入下一个宏任务。
事件循环的实际运作流程
事件循环并不是一次性执行完所有任务,而是按轮次进行:
- 执行当前宏任务(例如整个 script 脚本)。
- 执行过程中遇到异步操作,将其回调注册到对应的任务队列(宏或微)。
- 当前宏任务执行完毕后,立即执行所有可执行的微任务。
- 微任务清空后,进入下一次事件循环,取下一个宏任务执行。
这个机制确保了高优先级的微任务能尽快被执行,比如 Promise 回调通常比 setTimeout 更早响应。
异步编程的演进:回调 → Promise → async/await
早期 JavaScript 使用回调函数处理异步,但容易形成“回调地狱”,难以维护。
Promise 的出现让异步代码更清晰,支持链式调用:
fetch('/api/data') .then(res => res.json()) .then(data => console.log(data)) .catch(err => console.error(err));async/await 进一步简化了语法,让异步代码看起来像同步一样:
async function getData() { try { const res = await fetch('/api/data'); const data = await res.json(); console.log(data); } catch (err) { console.error(err); } }但要注意,await 并不会阻塞主线程,它只是语法糖,底层依然依赖 Promise 和事件循环。
基本上就这些。掌握 Event Loop 的机制,有助于写出更可靠、可预测的异步代码,也能更好理解为什么某些回调会先于其他代码执行。不复杂但容易忽略。











