JavaScript异步核心是Event Loop,它调度microtask(如Promise.then)优先于task(如setTimeout),Promise executor同步执行,async/await基于Promise实现非阻塞暂停恢复。
JavaScript异步编程的核心机制不是 Promise 或 async/await 本身,而是 Event Loop —— 它决定了回调何时执行、Promise 状态如何流转、async 函数如何暂停与恢复。
很多人以为 Promise.then 是“立即执行”,其实它被推入 microtask queue;而 setTimeout 的回调进的是 task queue(也叫 macrotask queue)。microtask 总是清空完才处理下一个 task。
这意味着:
Promise.then 回调一定比同轮循环中的 setTimeout 先运行Promise.then 会链式压入 microtask 队列,依次执行,不中断 JS 主线程queueMicrotask 可手动插入 microtask,行为和 Promise.resolve().then() 一致console.log(1); Promise.resolve().then(() => console.log(2)); setTimeout(() => console.log(3), 0); console.log(4); // 输出:1 → 4 → 2 → 3
写 new Promise((resolve, reject) => { ... }) 时,传入的函数会**立刻同步执行**,不是“等异步时机到了再调”。这是最容易误解的一点。
常见误用场景:
return 而忘记调用 resolve 或 reject → 
Promise 自带“延迟”或“自动异步” → 实际上它只是对异步流程做状态封装reject(new Error(...)),但不会触发全局 unhandledrejection(除非没加 .catch)new Promise(resolve => {
console.log('executor runs now'); // 立刻打印
resolve('done');
}).then(console.log); // 'done'
async 函数返回一个 Promise,await 并非阻塞线程,而是让出控制权,等右侧 Promise settled 后,再从上次暂停处继续执行(类似协程)。
关键细节:
await 后面如果不是 Promise,会被自动包装成 Promise.resolve(...)
await 只能在 async 函数内使用,顶层 await 仅在模块作用域(ESM)中合法await 默认串行;想并发需用 Promise.all([p1, p2]) 包一层try/catch 捕获的是 rejected Promise,不是 throw 的原始错误对象(但语义等价)async function f() {
const a = await Promise.resolve(1); // 不是阻塞,是暂停+注册 microtask
const b = await Promise.resolve(2);
return a + b;
}
f().then(console.log); // 3
真正容易被忽略的是:Promise 链的错误传播依赖每个环节是否显式处理,而 async/await 的 try/catch 只覆盖当前 await 表达式——漏掉一个 catch,就可能让 rejection 逃逸成 unhandledrejection。这在复杂嵌套或条件分支中尤其危险。