async/await 是 Promise 链的可读性补丁,不改变异步本质;async 函数必返回 Promise,await 仅限 async 内部使用,错误需显式 await + try/catch,多请求应依业务选串行或 Promise.all 并发。

async/await 不是语法糖的替代品,而是 Promise 链的可读性补丁——它不能消除异步本质,但能让你少写 .then() 和 .catch(),也避免掉进 Promise 嵌套或忘记 return 的坑。
async 函数必须显式返回 Promise 才能被 await 消费
很多人以为只要加了 async,函数就“自动变异步”,其实不然:async 只保证函数返回一个 Promise(哪怕你 return 42,也会被包装成 Promise.resolve(42))。但如果你在函数里调用的是普通同步函数、或者忘了 await 一个 Promise,那后续逻辑仍会同步执行。
-
async function foo() { return fetch('/api'); }→ 返回的是Promise,没问题 -
async function bar() { fetch('/api'); return 'done'; }→fetch被丢弃,'done'立即被包装成 resolved Promise,API 请求实际没等 - 正确写法是:
async function bar() { const res = await fetch('/api'); return res.json(); }
await 只能在 async 函数内部使用
这是常见报错源头:Uncaught SyntaxError: await is only valid in async function。你不能在顶层模块代码、普通函数、事件回调(如 button.onclick = () => { await fn(); })里直接写 await,除非那个函数本身被声明为 async。
-
浏览器中顶层
await仅在 ES 模块(type="module")下可用,且只限于模块作用域,不适用于普通 -
setTimeout(() => { await doAsync(); }, 100);❌ 报错;得写成setTimeout(async () => { await doAsync(); }, 100);✅ - React 事件处理函数常用写法:
const handleClick = async () => { await submitForm(); }
错误处理别只靠 try/catch,小心未捕获的 Promise rejection
await 会让 rejected Promise 变成同步抛出的异常,所以 try/catch 能捕获。但这只覆盖你显式 await 的那一层。如果某个 Promise 在 async 函数里被创建却没被 await,又没加 .catch(),它就会变成 unhandled rejection。
立即学习“Java免费学习笔记(深入)”;
-
async function bad() { fetch('/fail').catch(err => console.error(err)); }→ 安全,错误被吞掉 -
async function worse() { fetch('/fail'); }→ 即使函数是async,这个 Promise 仍可能触发unhandledrejection事件 - 推荐模式:
try { const data = await fetch(...).then(r => r.json()); } catch (e) { /* 处理网络或解析错误 */ }
await 并不“阻塞线程”,但会暂停当前 async 函数的执行流
这是最容易误解的一点:JS 仍是单线程、非阻塞的。await 只是让 JS 引擎把当前函数挂起,把控制权交还给事件循环,等 Promise settled 后再从暂停处继续。这意味着:
- 多个
await默认是串行的,不是并行的 ——await a(); await b();意味着b()必须等a()完全结束 - 想并发?得用
Promise.all([a(), b()]),再await Promise.all([...]) - 过度使用
await在无关操作间会造成不必要延迟,比如本可以并行的 API 请求被写成串行
真正难的从来不是写 async/await,而是判断哪几个异步操作该串行、哪几个该并发、以及 Promise 生命周期里哪一环该负责错误清理。这些没法靠语法自动解决,得看业务逻辑本身是否允许重叠或降级。











