异步与同步的本质区别在于是否让出主线程控制权:异步操作允许主线程立即继续执行,同步操作则必须等待完成才执行后续代码;同步任务直接入调用栈、顺序执行、结果即时可得。

异步与同步的边界,不在于代码写在哪、长得像不像,而在于是否让出主线程控制权。只要一个操作执行时,主线程能立刻继续往下跑,它就是异步;反之,必须等它彻底做完才放行后续代码,就是同步。
同步任务:执行即占用,完成才放手
同步任务直接进入调用栈,逐行执行,中间不暂停、不移交控制权。变量赋值、函数调用(非异步API)、DOM属性读取、简单循环或计算——这些都属于同步行为。它们的特点是结果可立即获取,逻辑顺序和执行顺序完全一致。
- 哪怕只是
for (let i = 0; i ,也会卡住整个页面,因为主线程被独占 - 没有回调机制,也不进任何队列,执行完就出栈
- 即使是看似“耗时”的操作,只要没交给宿主环境其他线程处理,它仍是同步的
异步任务:发起即交出,结果靠通知
异步任务本身不运行在JS主线程上,而是由浏览器或Node.js的底层线程(如网络线程、定时器线程、文件IO线程)负责执行。JS主线程只做两件事:发指令、收回调。关键判断点是——调用后是否立刻返回,且不阻塞下一行代码执行。
-
setTimeout、fetch、addEventListener、Promise.resolve().then()都是典型的异步入口 - 注意:
Promise构造函数里的执行器函数(executor)是同步的,但.then回调是微任务,属于异步 -
async函数内部的await表达式会暂停函数执行,但不会阻塞主线程——它把后续逻辑包装成微任务,本质仍是异步调度
宏任务与微任务:异步内部的优先级分层
所有异步任务最终都会归入两类队列:宏任务队列(如 setTimeout、setInterval、I/O、UI渲染)和微任务队列(如 Promise.then、MutationObserver)。它们共同构成事件循环的调度依据,但优先级不同。
- 每次宏任务执行完毕后,会清空当前全部微任务,再取下一个宏任务
- 这意味着:一个
setTimeout回调(宏任务)里触发的Promise.then,会在该宏任务结束后立刻执行,而不是等到下一轮事件循环 - 这种分层不是为了“快慢”,而是为了保证响应及时性(比如确保状态更新后立刻触发视图更新)
识别边界的实用方法
不用背API列表,只需问自己一个问题:这行代码执行时,JS引擎会不会把控制权还给我,让我继续跑后面的同步代码?
- 如果答案是“会”,那就是异步——哪怕它实际耗时很短(如空
Promise.resolve().then()) - 如果答案是“不会,必须等它干完我才动”,那就是同步——哪怕它只是读一个属性(
element.offsetWidth) -
await不是异步的开关,而是语法糖;真正决定异步的是它等待的那个 Promise 是否已 resolve,以及该 Promise 的链式回调如何被调度











