async/await 是基于 Promise 的语法糖,使异步代码更易读;async 函数自动返回 Promise,await 暂停函数执行但不阻塞线程,需在 async 函数内使用,支持 thenable 对象,错误可用 try/catch 捕获,并发请求应避免串行化。

async 和 await 是 JavaScript 中处理异步操作的语法糖,本质是基于 Promise 的封装。它们不改变异步本质,但让异步代码写起来像同步代码,可读性更强、错误处理更直观。
async 函数必须返回 Promise
声明一个函数为 async,它就自动包装返回值为 Promise。即使你 return 42,实际返回的是 Promise.resolve(42);如果抛错,则等价于 Promise.reject(err)。
常见误区:以为 async 让函数“变同步”了——其实只是让 await 能暂停当前函数执行(仅限该函数内部),外部调用仍是异步的。
-
async函数内部可以没有await,但仍有 Promise 包装行为 - 不能在普通函数或顶层作用域中直接使用
await,必须在async函数内 - 箭头函数也可
async:const fetchUser = async () => { ... }
await 只能等待 thenable 对象
await 后面的表达式会被自动调用 .then(),所以它接受任何 Promise 或拥有 .then() 方法的对象(比如 jQuery 的 Deferred,或某些旧库的类 Promise 实例)。
立即学习“Java免费学习笔记(深入)”;
但要注意:
- 如果等的是普通值(如
await 123),会立即 resolve,等效于Promise.resolve(123) - 如果等的是
null、undefined或没.then的对象,会报TypeError: xxx is not a thenable -
await不会“阻塞线程”,只是暂停当前async函数的执行,JS 引擎继续跑其他任务
错误处理:try/catch 比 .catch() 更自然
用 await 时,异步错误可以直接用 try/catch 捕获,不需要链式 .catch(),嵌套深时优势明显。
async function loadConfig() {
try {
const res = await fetch('/api/config');
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error('加载配置失败:', err.message);
throw err; // 重新抛出以便上层处理
}
}
注意点:
-
catch会捕获await表达式 reject 的 Promise,也包括throw的同步错误 - 多个
await写在同一try块里,任一失败都会进catch,无法区分是哪个出错——需要拆分或加日志 - 不要在
await后直接接.catch()(如await fn().catch(...)),这会让错误“吞掉”,catch块拿不到原始错误堆栈
并发请求:避免无脑 await 导致串行化
连续写多个 await 会让请求变成串行,哪怕它们彼此无关。想并发,得先触发所有 Promise,再统一 await。
async function fetchBoth() {
// ❌ 串行:第二个请求要等第一个完成
const user = await fetch('/user');
const posts = await fetch('/posts');
// ✅ 并发:两个请求同时发出
const [userRes, postsRes] = await Promise.all([
fetch('/user'),
fetch('/posts')
]);
return { user: await userRes.json(), posts: await postsRes.json() };
}
关键区别:
-
Promise.all失败时会短路(任一 reject 就 reject 整个),需用Promise.allSettled处理部分失败场景 - 浏览器中并发请求数有限制(通常 6~8 个),大量并行可能触发限流或超时
- 如果请求有依赖关系(比如用 token 换用户信息),才必须串行
await
真正容易被忽略的不是语法,而是对事件循环的理解:await 不是“停住 JS”,而是“把后续逻辑注册为微任务,让出执行权”。一旦搞混这点,就会在调试竞态条件、定时器、或与 setTimeout 交互时踩坑。











