# 请求失败重试策略

URL: https://caijiao.org/axios/troubleshooting/retry
Source: docs/axios/troubleshooting/retry.md
Description: 本章节介绍如何在 Axios 中实现请求失败的自动重试机制，包括请求超时、网络断开、服务器错误等情况下的重试逻辑。通过手动封装、使用 axios-retry 插件、自定义重试间隔与最大重试次数等方法，提高前端网络请求的稳定性与用户体验。

在复杂网络环境中（如移动端或弱网场景），请求可能因为超时、断网或服务器异常而失败。
为提升稳定性，Axios 可以通过**重试机制**在失败时自动重新发送请求。

## 一、为什么需要重试机制

常见导致请求失败的原因：

* 网络波动或短暂中断；
* 服务器临时过载；
* 第三方 API 响应超时；
* DNS 问题或负载均衡切换。

而这些问题往往是**暂时性的**，适当重试可以让请求成功率显著提升。

## 二、最简单的重试逻辑（手动实现）

```js
import axios from "axios";

async function requestWithRetry(config, retryCount = 3, delay = 1000) {
  for (let i = 0; i < retryCount; i++) {
    try {
      const response = await axios(config);
      return response;
    } catch (error) {
      console.warn(`请求失败 [${i + 1}/${retryCount}]：`, error.message);
      if (i === retryCount - 1) throw error;
      await new Promise(res => setTimeout(res, delay)); // 延时后重试
    }
  }
}
```

使用示例：

```js
requestWithRetry({ url: "/api/user", timeout: 2000 })
  .then(res => console.log("请求成功：", res.data))
  .catch(err => console.error("多次重试仍失败：", err.message));
```

## 三、结合拦截器实现自动重试

通过 **Axios 拦截器**，可以统一为所有请求添加重试逻辑：

```js
const service = axios.create({
  baseURL: "/api",
  timeout: 5000
});

service.interceptors.response.use(
  res => res,
  async error => {
    const config = error.config;
    if (!config || config.__retryCount >= 3) {
      return Promise.reject(error);
    }

    config.__retryCount = (config.__retryCount || 0) + 1;
    console.log(`第 ${config.__retryCount} 次重试...`);

    await new Promise(res => setTimeout(res, 1000));
    return service(config);
  }
);
```

使用示例：

```js
service.get("/data")
  .then(res => console.log("成功：", res.data))
  .catch(err => console.error("重试失败：", err.message));
```

---

## 四、使用第三方库 `axios-retry`

如果不想手动封装，可以直接使用官方社区维护的 [`axios-retry`](https://github.com/softonic/axios-retry) 插件。

### 安装

```bash
npm install axios-retry
```

### 基本用法

```js
import axios from "axios";
import axiosRetry from "axios-retry";

const instance = axios.create({ baseURL: "/api" });

axiosRetry(instance, {
  retries: 3, // 最大重试次数
  retryDelay: retryCount => retryCount * 1000, // 递增延时
  retryCondition: error => error.code === "ECONNABORTED" || error.response?.status >= 500
});

instance.get("/data")
  .then(res => console.log("成功:", res.data))
  .catch(err => console.error("最终失败:", err.message));
```

## 五、重试策略设计建议

| 场景                     | 是否适合重试 | 建议策略          |
| ---------------------- | ------ | ------------- |
| 网络超时                   | ✅      | 3 次，每次间隔 1 秒  |
| 服务器 500 错误             | ✅      | 2~3 次，带随机延迟   |
| 404 / 401 / 403 错误     | ❌      | 无需重试（逻辑错误）    |
| 文件上传                   | ⚠️     | 建议分片重试，不整体重传  |
| 幂等操作（GET/HEAD）         | ✅      | 可安全重试         |
| 非幂等操作（POST/PUT/DELETE） | ⚠️     | 谨慎使用，可能造成重复提交 |

## 六、结合指数退避算法（Exponential Backoff）

避免高频请求冲击服务器，可使用指数增长的延迟：

```js
function exponentialDelay(retryCount) {
  return Math.pow(2, retryCount) * 1000; // 1s, 2s, 4s, 8s...
}
```

应用到 `axios-retry`：

```js
axiosRetry(axios, {
  retries: 5,
  retryDelay: exponentialDelay
});
```

## 七、进阶：记录重试日志与监控上报

在生产环境中，可将失败与重试信息上报到日志系统或监控服务：

```js
axios.interceptors.response.use(null, async error => {
  const cfg = error.config || {};
  cfg.__retryCount = (cfg.__retryCount || 0) + 1;

  if (cfg.__retryCount <= 3) {
    console.log(`[Retry] ${cfg.url} - attempt ${cfg.__retryCount}`);
    await new Promise(r => setTimeout(r, 1000));
    return axios(cfg);
  }

  // 上报日志系统
  sendLog({
    url: cfg.url,
    method: cfg.method,
    retries: cfg.__retryCount,
    message: error.message
  });

  return Promise.reject(error);
});
```

## 八、小结

Axios 的重试机制是提升前端网络稳定性的关键手段。
**最佳实践：**

* 对**可恢复性错误（5xx、超时）**启用重试；
* 控制**重试次数与延迟**，避免对服务器造成压力；
* 使用 `axios-retry` 或拦截器实现全局自动重试；
* 非幂等请求谨慎重试，避免数据重复提交。

通过科学的重试策略，Axios 可以在不稳定网络中保持高可用性与更流畅的用户体验。
