async/await 是简化异步控制流的关键机制,async 函数必返回 Promise,await 仅限 async 内使用,需配合 try/catch,支持 thenable 对象但需防 undefined。
async/await 不是语法糖的“装饰”,而是让异步流程回归同步阅读习惯的关键机制——它真正简化的是控制流逻辑,而不是减少代码行数。
很多人误以为只要加了 async,函数就“自动变异步”了,其实不然:async 只保证函数返回一个 Promise(哪怕你 return 42,也会被包装成 Promise.resolve(42))。如果你在函数里直接 return fetch(...),那返回的就是原生 Promise;但若中间有 await,就必须确保每个分支都走异步路径,否则容易漏掉错误处理。
if (condition) return someSyncValue; —— 这会让返回类型不一致(有时是 number,有时是 Promise)return Promise.resolve(value) 或直接 return value(async 会帮你包)return await Promise.reject(new Error(...)),避免未被捕获的 rejected promise这是最常踩的语法错误:await 不是全局操作符,它依赖 async 函数提供的执行上下文。在普通函数、事件回调、模块顶层(ESM)中直接写 await 会报 SyntaxError: await is only valid in async function。
setTimeout(() => { await doSomething(); }, 100) 里直接 awaitasync 函数,或改用 setTimeout(async () => { ... }, 100)
"type": "module" 并配合 top-level await(Node.js 14.8+ / 现代浏览器支持),但注意这会阻塞模块初始化和 .then().catch() 不同,await 遇到 rejected Promise 会直接抛出异常,不 catch 就崩。很多初学者只写 await apiCall() 却没包 try,结果网络失败时整个调用栈静默中断。
try { const res = await fetch(...); } catch (e) { console.error(e); }
.catch() 更紧凑const [a
, b] = await Promise.all([fetch('/a'), fetch('/b')]),而非连续 await(那是串行)Promise.allSettled 和 Promise.race 的语义差异——它们返回结构固定的结果,和直接 await 行为不同await 实际上会调用右侧值的 then 方法(如果存在),所以它支持任何带 .then() 的对象(比如 jQuery Deferred、Axios response、甚至手写的类 Promise 对象)。但这也带来隐性风险:如果对象有 then 属性却不是 Promise(比如某个 API 返回 { then: 'not a function' }),就会触发 “unhandled promise rejection”。
Promise.resolve(val) 包一层再 awaitCannot read property 'then' of undefined,大概率是 await 了 undefined 或 null
val instanceof Promise 在运行时不可靠,因为 Promise 可能被 polyfill 替换真正的复杂点不在语法,而在于异步边界如何划分:什么时候该用 await 阻塞当前流程,什么时候该用 Promise.all 并行推进,又什么时候该把 await 推到调用方去处理——这些决策直接影响错误传播路径和资源释放时机。