用 Fetch 加 async/await 把异步写的舒服
过去那时候调接口都是 XMLHttpRequest 配回调函数,多个请求有依赖就一层套一层,变成"回调地狱",缩进越来越深、错误处理还散,代码一多钱起来就看的头皮发麻。后来有了 Promise,再后来有了 async/await好了很多了,异步代码能写成看起来像同步的样子,读起来顺、改起来也方便。浏览器自带的 Fetch 返回的就是 Promise,配合 async/await 是现在前端调接口的标准写法。
一、回调到 async/await 的变化
回调时代大概是:
fetch("/api/user").then((r) => r.json()).then((data) => {
console.log(data)
})
用 then 链比嵌套回调好些,但逻辑一多还是一串。换成 async/await:
async function getUser() {
const res = await fetch("/api/user")
const data = await res.json()
return data
}
await 会"等"这个 Promise 完成再往下走,代码从上往下读,没有缩进地狱。async 函数本身返回 Promise,调用方还能继续 .then 或再 await。
二、请求带参数和请求体
GET 带查询参数拼到 URL 上;POST 用 method + body(默认 Fetch 不带头、不自动带 cookie,要显式设):
async function createUser(payload) {
const res = await fetch("/api/user", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload),
})
if (!res.ok) throw new Error("创建失败 " + res.status)
return res.json()
}
几个点:Fetch 默认不发送 Cookie,跨凭证请求要加 credentials: ‘include’;Content-Type 要自己设,否则后端可能按表单解析;res.ok 是状态码 200-299 为 true,非 2xx 不会抛异常,得自己判断并 throw,否则错误被当成功吞掉。
三、错误处理
async 函数里用 try/catch 接住网络错误和主动抛的错:
async function safeGet() {
try {
const data = await getUser()
render(data)
} catch (e) {
showError("加载失败:" + e.message)
}
}
网络断开、超时、后端 500,都会进 catch。把错误展示出来比静默失败好,用户至少知道出了事。
四、多个请求并发:Promise.all
两个接口没依赖,别串行等,用 Promise.all 一起发:
const [user, orders] = await Promise.all([
fetch("/api/user").then((r) => r.json()),
fetch("/api/orders").then((r) => r.json()),
])
Promise.all 等全部完成,结果按顺序放回数组;其中一个 reject 整个就 reject。要"各自失败不影响彼此"用 Promise.allSettled。注意 Promise.all 不限制并发数,成百个请求一起发会被浏览器或后端限流,那种场景要自己分批或加并发控制。
五、几个要注意的点
第一,Fetch 的超时得自己写,它原生不支持 timeout,常用 AbortController 取消请求实现超时。第二,默认不带 Cookie,跨域带凭证要 credentials:‘include’ 且服务端允许。第三,res.json() 只能调一次,调完流就消费了,要多次读得先 clone。第四,大文件上传用 FormData 而不是手写 JSON,浏览器会自己设 multipart 边界。第五,await 只能写在 async 函数里,顶层要用就用 IIFE 或模块顶层的 top-level await(现代环境支持)。第六,别在循环里 await 一堆本可并行的请求,那是把并发硬生生写成串行,慢。
六、小结
前端调接口用 Fetch + async/await 最顺:await fetch 拿响应、await res.json 取数据,代码从上往下读没有回调地狱。POST 设好 method/headers/body、用 res.ok 判断成败、try/catch 兜底错误、无依赖的请求用 Promise.all 并发。超时靠 AbortController、凭证靠 credentials。比老式回调清晰得多,也是现在的主流写法。
- 点赞
- 收藏
- 关注作者
评论(0)