async/await 是基于 Promise 的语法糖,使异步代码更易读;async 函数自动返回 Promise,await 暂停函数执行但不阻塞线程,需在 async 函数内使用,支持 thenable 对象,错误可用 try/catch 捕获,并发请求应避免串行化。
async 和 await 是 JavaScript 中处理异步操作的语法糖,本质是基于 Promise 的封装。它们不改变异步本质,但让异步代码写起来像同步代码,可读性更强、错误处理更直观。
声明一个函数为 async,它就自动包装返回值为 Promise。即使你 return 42,实际返回的是 Promise.resolve(42);如果抛错,则等价于 Promise.reject(err)。
常见误区:以为 async 让函数“变同步”了——其实只是让 await 能暂停当前函数执行(仅限该函数内部),外部调用仍是异步的。
async 函数内部可以没有 await,但仍有 Promise 包装行为await,必须在 async 函数内async:const fetchUser = async () => { ... }
await 后面的表达式会被自动调用 .then(),所以它接受任何 Promise 或拥有 .then() 方法的对象(比如 jQuery 的 Deferred,或某些旧库的类 Promise 实例)。
但要注意:
await 123),会立即 resolve,等效于 Promise.resolve(123)
null、undefined 或没 .then 的对象,会报 TypeError: xxx is not a thenable
await 不会“阻塞线程”,只是暂停当前 async 函数的执行,JS 引擎继续跑其他任务用 await 时,异步错误可以直接用 try/catch 捕获,不需要链式 .c,嵌套深时优势明显。
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 会让请求变成串行,哪怕它们彼此无关。想并发,得先触发所有 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 处理部分失败场景await
真正容易被忽略的不是语法,而是对事件循环的理解:await 不是“停住 JS”,而是“把后续逻辑注册为微任务,让出执行权”。一旦搞混这点,就会在调试竞态条件、定时器、或与 setTimeout 交互时踩坑。