资讯详情

资讯详情

Axios 的 Promise 编程模型:从 then/catch/finally 到 async/await 的完整实践指南

Axios 的 Promise 编程模型从 then/catch/finally 到 async/await 的完整实践指南【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axiosaxios 是一个基于 Promise 的 HTTP 客户端它构建在 ES6 原生 Promise API 之上每一次请求都会返回一个 Promise它要么以响应对象response object结算resolve要么以错误拒绝reject。对于不支持原生 Promise 的环境需要自行 polyfill例如 es6-promise。本文基于仓库中的 Promises 官方文档结合 Axios.js、settle.js 等源码系统讲解 axios 的 Promise 编程模型——包括.then()/.catch()/.finally()链式处理、async/await推荐写法、Promise.all并行请求、Promise.allSettled容错处理、请求链式编排以及AxiosPromise泛型在 TypeScript 中的类型保真机制并深入源码说明请求 Promise 是如何被 resolve 或 reject 的。请求的返回类型一次请求就是一个 Promiseaxios 的每个请求方法axios.get、axios.post等本质上都是对Axios.prototype.request的包装而request本身是一个async方法因此调用返回的就是一枚标准的 ES6 Promiseaxios.get(/api/users); // PromiseAxiosResponse从 lib/core/Axios.js 的源码结构看各 HTTP 方法最终都汇聚到this.request(...)// lib/core/Axios.js节选 async request(configOrUrl, config) { try { return await this._request(configOrUrl, config); } catch (err) { // ... 补充/合并错误堆栈后重新 throw throw err; } }request内部的 catch 分支还做了一件对排错很有用的事当捕获到Error实例且其stack缺失或被截断时会利用Error.captureStackTrace生成当前调用栈并合并进err.stack。这意味着.catch((error) ...)中拿到的错误对象通常带有更完整的堆栈信息便于定位抛出点。而真正决定这条 Promise 链如何流动的是_request中的拦截器链编排lib/core/Axios.js 第 192-209 行附近let promise; let i 0; let len; if (!synchronousRequestInterceptors) { const chain [dispatchRequest.bind(this), undefined]; chain.unshift(...requestInterceptorChain); chain.push(...responseInterceptorChain); len chain.length; promise Promise.resolve(config); while (i len) { promise promise.then(chain[i], chain[i]); } return promise; }也就是说当存在异步请求拦截器时axios 从Promise.resolve(config)出发依次.then(拦截器 fulfilled, 拦截器 rejected)中间插入dispatchRequest最后挂上响应拦截器——整条链上任何一个环节 reject最终都会传播到你await后的 catch 或.catch()。这正是axios 返回标准 Promise这一承诺在源码层面的实现方式。Promise 的 resolve / reject 由谁决定请求发出后Promise 的最终结算发生在适配器层。lib/core/settle.js 是关键的结算函数export default function settle(resolve, reject, response) { const validateStatus response.config.validateStatus; if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); } else { reject(new AxiosError( Request failed with status code response.status, response.status 400 response.status 500 ? AxiosError.ERR_BAD_REQUEST : AxiosError.ERR_BAD_RESPONSE, response.config, response.request, response )); } }这里揭示了两个实践要点HTTP 4xx/5xx 不会自动 resolve只有当validateStatus(response.status)为真时默认规则在 lib/defaults/index.js 中定义才 resolve否则 reject 一个AxiosError并根据状态码区分ERR_BAD_REQUEST4xx与ERR_BAD_RESPONSE5xx。可以在.catch()中拿到完整上下文被 reject 的AxiosError携带config、request、response属性因此文档中.catch((error) { console.error(Request failed:, error.message); })这类写法可以进一步访问error.response.status、error.config等。此外lib/core/dispatchRequest.js 中adapter(config).then(onAdapterResolution, onAdapterRejection)还保证了即使适配器 reject网络错误、取消等只要错误对象上挂了response也会对其执行transformResponse数据转换然后Promise.reject(reason)传播下去——所以失败响应体也能反序列化这一行为是有源码背书的。TypeScript 集成AxiosPromise 的类型保真对于 TypeScript 项目index.d.ts 第 580 行定义了核心类型export type AxiosPromiseT any, D any, P any PromiseAxiosResponseT, D, {}, P;AxiosPromiseT, D, P就是AxiosResponseT, D, {}, P的 Promise其中三个泛型参数分别对应响应数据类型 T、请求体类型 D、查询参数类型 P。它的关键价值在于请求的数据和参数会保留在response.config上使类型系统能够追踪一次请求的完整往返。AxiosResponse接口的定义index.d.ts 第 515-522 行export interface AxiosResponseT any, D any, H {}, P any { data: T; status: number; statusText: string; headers: (H RawAxiosResponseHeaders) | AxiosResponseHeaders; config: InternalAxiosRequestConfigD, P; request?: any; }文档给出的示例展示了三个泛型参数如何在实际业务类型中落地declare const search: AxiosPromiseSearchResponse, RequestBody, SearchParams; search.then((response) { response.data; // SearchResponse response.config.data; // RequestBody | undefined response.config.params; // SearchParams | undefined });从上面的接口定义可以验证response.data的类型正是第一个泛型参数Tresponse.config是InternalAxiosRequestConfigD, P因此config.data请求体与config.params查询参数分别携带D与P类型。这对根据本次请求实际发出去什么来推断响应类型的场景非常有用——同一个接口config里保留了本次调用的真实载荷类型。then / catch / finally标准的三段式处理因为 axios 返回标准 Promise.then()、.catch()和.finally()可以直接使用axios.get(/api/users) .then((response) { console.log(response.data); }) .catch((error) { console.error(Request failed:, error.message); }) .finally(() { console.log(Request finished); });使用建议.then()中处理成功分支参数是完整的AxiosResponsedata、status、statusText、headers、config.catch()中处理所有失败分支——包括网络错误、超时、取消CanceledError、以及validateStatus判定失败的 4xx/5xx 响应.finally()适合放置与成败无关的收尾逻辑如关闭 loading 状态。async / await大多数代码库的推荐写法官方文档推荐在多数代码库中使用async/await它让异步代码读起来像同步代码async function fetchUser(id) { try { const response await axios.get(/api/users/${id}); return response.data; } catch (error) { console.error(Failed to fetch user:, error.message); throw error; } }这种写法有两个工程价值控制流更直观try/catch覆盖了同步代码和异步错误避免回调嵌套错误可以精确传播示例中catch里先记录日志再throw error保持了记录但不吞掉的错误处理习惯。由于dispatchRequest与settle中 reject 的都是结构化的AxiosError上游catch可以进一步通过error.isAxiosError、error.code、error.response做精细分支。并行请求Promise.all 与 Promise.allSettledaxios 返回标准 Promise因此可以直接用Promise.all同时发起多个请求并等待全部完成const [users, posts] await Promise.all([ axios.get(/api/users), axios.get(/api/posts), ]); console.log(users.data, posts.data);注意语义差异Promise.all会在任一请求失败时立即 reject。如果需要处理部分失败部分接口 5xx 不应导致整页崩溃应改用Promise.allSettledconst results await Promise.allSettled([ axios.get(/api/users), axios.get(/api/posts), ]); results.forEach((result) { if (result.status fulfilled) { console.log(result.value.data); } else { console.error(Request failed:, result.reason.message); } });allSettled为每个请求返回{ status: fulfilled | rejected, value/reason }结构成功项的value是AxiosResponse取.data失败项的reason是 reject 出的错误对象通常是AxiosError取.message或其他属性。两种方式的取舍可以概括为方法失败行为适用场景Promise.all任一失败即整体 reject多个请求是全有或全无的强依赖组合Promise.allSettled等待全部结束逐项报告结果聚合多个独立数据源允许局部降级请求链式编排串联依赖请求你可以链式调用.then()来顺序执行多个请求把上一个请求的数据传递给下一个——这是先查用户、再按用户查其文章这类依赖型数据流的经典写法axios.get(/api/user/1) .then(({ data: user }) axios.get(/api/posts?userId${user.id})) .then(({ data: posts }) { console.log(Posts for user:, posts); }) .catch(console.error);这里有一个容易忽略的细节在.then()的回调中返回一个新的 axios 请求而不是先赋值再 resolvePromise 链会自动等待该新请求完成后才进入下一个.then()。链上任意一环 reject包括第二步请求失败都会跳过中间的成功回调直接落到链尾的.catch()。对应的async/await等价写法是try { const { data: user } await axios.get(/api/user/1); const { data: posts } await axios.get(/api/posts?userId${user.id}); console.log(Posts for user:, posts); } catch (error) { console.error(error); }两种写法在 Promise 语义上完全等价选择哪一种是团队风格问题但错误处理路径catch 的位置、是否 rethrow应当保持一致。小结axios 的每次请求都是标准 ES6 Promise成功 resolve 完整的AxiosResponse失败 reject 携带config/response的AxiosError或取消时的CanceledError4xx/5xx 是否算失败由validateStatus决定结算逻辑在 lib/core/settle.js 中推荐用async/await try/catch组织控制流用.finally()收尾强依赖的并行请求用Promise.all允许局部失败的聚合用Promise.allSettled依赖型顺序请求用.then()链式编排或等价的await序列TypeScript 中用AxiosPromiseT, D, P保留响应类型、请求体与查询参数的完整类型链路。想进一步理解拦截器如何嵌入这条 Promise 链可以继续查看 interceptors.md 文档与 lib/core/Axios.js 中_request的链式编排实现错误处理细节可参考 error-handling.md。【免费下载链接】axiosPromise based HTTP client for the browser and node.js项目地址: https://gitcode.com/GitHub_Trending/ax/axios创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →