开场白:
大家好,我是你们的老朋友,一个每天都在和 Bug 斗智斗勇的前端程序员。今天我要给大家分享一个我最近写的“神器”——一个超级强大的 API 客户端工具。它不仅让我的工作效率翻倍,还让我的老板直呼“内行”!如果你也在为网络请求的复杂性头疼,那这篇文章你一定要看完!
正文:
1. 为什么我要写这个工具?
作为一个前端程序员,我每天都要和 API 打交道。GET、POST、PUT、DELETE……这些 HTTP 方法就像我的“老朋友”,但有时候它们也会让我抓狂。比如:
- 请求超时了怎么办?
- 网络波动导致请求失败了怎么办?
- 用户突然离开页面,未完成的请求怎么取消?
- 重复请求同一个接口,能不能缓存一下?
于是,我决定自己动手,写一个“万能”的 API 工具,解决这些问题!经过一番折腾,终于搞定了,效果还不错,老板看了直呼“内行”!
2. 这个工具到底有多牛?
先来看看它的核心功能:
- 请求超时控制:再也不用担心请求卡死了,超时自动取消!
- 请求重试机制:网络波动?不怕!自动重试,最多 3 次!
- 请求取消功能:用户突然离开页面?一键取消未完成的请求!
- 请求缓存:重复请求同一个接口?直接返回缓存结果,性能提升 100%!
- 并发控制:同时发起太多请求?自动排队,避免资源耗尽!
- 灵活的拦截器:想在请求前后加点“私货”?拦截器帮你搞定!
是不是听起来就很厉害?别急,代码在后面,咱们慢慢看!
3. 代码展示:一看就懂,一学就会!
以下是这个工具的完整代码(基于 JavaScript):
class Api {
constructor(baseUrl, headers = {}, maxConcurrentRequests = 5) {
this.baseUrl = baseUrl;
this.headers = {
'Content-Type': 'application/json',
...headers,
};
this.requestInterceptors = [];
this.responseInterceptors = [];
this.maxConcurrentRequests = maxConcurrentRequests;
this.currentRequests = 0;
this.requestQueue = [];
this.cache = new Map();
this.abortControllers = new Map();
}
async request(endpoint, method, body = null, credentials = 'omit', timeout = 10000, retries = 3, useCache = false, customConfig = {}) {
const cacheKey = `${method}:${endpoint}`;
// 使用缓存
if (useCache && this.cache.has(cacheKey)) {
return this.cache.get(cacheKey);
}
// 并发控制
if (this.currentRequests >= this.maxConcurrentRequests) {
await new Promise((resolve) => this.requestQueue.push(resolve));
}
this.currentRequests++;
let lastError;
for (let i = 0; i < retries; i++) {
try {
const result = await this._request(endpoint, method, body, credentials, timeout, customConfig);
if (useCache) {
this.cache.set(cacheKey, result);
}
return result;
} catch (error) {
lastError = error;
if (i === retries - 1) {
throw lastError;
}
}
}
}
async _request(endpoint, method, body, credentials, timeout, customConfig) {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
// 存储 controller 以便后续取消
this.abortControllers.set(endpoint, controller);
let config = {
method,
headers: this.headers,
credentials,
signal: controller.signal,
...customConfig,
};
if (body) {
config.body = JSON.stringify(body);
}
// 执行请求拦截器
for (let interceptor of this.requestInterceptors) {
config = await interceptor(config, endpoint, method);
}
const url = `${this.baseUrl}${endpoint}`;
let response;
try {
response = await fetch(url, config);
if (!response.ok) {
throw new ApiError(`HTTP 错误! 状态码: ${response.status}`, response.status, response, config);
}
} catch (error) {
console.error('请求失败:', error);
throw error;
} finally {
clearTimeout(timeoutId);
this.abortControllers.delete(endpoint);
this.currentRequests--;
if (this.requestQueue.length > 0) {
this.requestQueue.shift()();
}
}
// 执行响应拦截器
for (let interceptor of this.responseInterceptors) {
response = await interceptor(response, endpoint, method);
}
return await response.json();
}
async get(endpoint, queryParams = {}, credentials = 'omit', timeout = 10000, retries = 3, useCache = false) {
let fullEndpoint = endpoint;
if (Object.keys(queryParams).length > 0) {
const queryString = new URLSearchParams(queryParams).toString();
fullEndpoint += `?${queryString}`;
}
return this.request(fullEndpoint, 'GET', null, credentials, timeout, retries, useCache);
}
async post(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
return this.request(endpoint, 'POST', body, credentials, timeout, retries);
}
async put(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
return this.request(endpoint, 'PUT', body, credentials, timeout, retries);
}
async patch(endpoint, body, credentials = 'omit', timeout = 10000, retries = 3) {
return this.request(endpoint, 'PATCH', body, credentials, timeout, retries);
}
async delete(endpoint, credentials = 'omit', timeout = 10000, retries = 3) {
return this.request(endpoint, 'DELETE', null, credentials, timeout, retries);
}
useRequestInterceptor(interceptor) {
this.requestInterceptors.push(interceptor);
}
useResponseInterceptor(interceptor) {
this.responseInterceptors.push(interceptor);
}
cancelRequest(endpoint) {
if (this.abortControllers.has(endpoint)) {
this.abortControllers.get(endpoint).abort();
this.abortControllers.delete(endpoint);
}
}
clearCache() {
this.cache.clear();
}
}
class ApiError extends Error {
constructor(message, statusCode, response, config) {
super(message);
this.statusCode = statusCode;
this.response = response;
this.config = config;
}
}
export default Api;
4. 使用示例:简单到哭!
const api = new Api('https://api.example.com', { Authorization: 'Bearer token' });
// 添加请求拦截器
api.useRequestInterceptor(async (config, endpoint, method) => {
console.log(`请求拦截器: ${method} ${endpoint}`);
return config;
});
// 发起 GET 请求(带缓存)
api.get('/users', { page: 1 }, 'include', 10000, 3, true)
.then(data => console.log(data))
.catch(error => console.error(error));
// 发起 POST 请求
api.post('/users', { name: 'John' })
.then(data => console.log(data))
.catch(error => console.error(error));
5. 总结:这个工具为什么值得你拥有?
- 高效:解决网络请求中的各种痛点,提升开发效率。
- 灵活:支持自定义配置、拦截器、缓存等功能。
- 稳定:超时、重试、取消等机制,确保请求的稳定性。
如果你觉得这个工具不错,欢迎点赞、转发、打赏!你的支持是我持续分享的动力!
结尾:
好了,今天的分享就到这里。如果你觉得这篇文章对你有帮助,别忘了关注我,我会持续分享更多实用的技术干货!如果你有任何问题或建议,欢迎在评论区留言,我会一一回复!
求关注、求转发、求打赏!你们的支持是我最大的动力!
互动话题:
你在开发中遇到过哪些网络请求的坑?欢迎在评论区分享你的故事!