You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3+Axios:请求出错时取消所有待处理请求的实现方案

解决方案:Axios并发请求异常时取消所有待处理请求 + 修复加载指示器问题

一、核心问题分析

  1. 加载指示器异常:原代码用单个布尔值isLoading控制状态,并发请求时第一个请求完成就将其设为false,导致后续请求仍在进行但加载状态已关闭。
  2. 异常时未取消请求:单个请求报错后,剩余待处理请求仍会继续执行,不符合需求。

二、方案1:基于AbortController批量取消并发请求

通过维护待处理请求的映射表,结合AbortController实现请求的批量取消,同时改用计数模式修复加载状态问题。

1. 修改Pinia配置Store

先在配置Store中添加请求计数状态,用getter计算加载状态:

// @/stores/configuration-store.ts
import { defineStore } from 'pinia';
import { EnvironmentEnum } from '@/interfaces/environment-enum';

export const useConfigStore = defineStore('config', {
  state: () => ({
    selectedEnvironment: EnvironmentEnum.Production, // 替换你的默认环境
    loadingCount: 0, // 请求计数,替代原布尔值isLoading
  }),
  getters: {
    isLoading: (state) => state.loadingCount > 0, // 通过计数自动判断加载状态
  },
});

2. 改造Axios实例(axiosClient.ts)

import { notify } from '@/services/notificationService';
import axios, { AxiosError, AxiosRequestConfig } from 'axios';
import { storeToRefs } from 'pinia';
import { EnvironmentEnum } from './interfaces/environment-enum';
import { useConfigStore } from '@/stores/configuration-store';
import axiosRetry from 'axios-retry';

const axiosClient = axios.create({
    withCredentials: true,
});

// 注意:将重试逻辑绑定到当前axios实例,而非全局axios
axiosRetry(axiosClient, {
    retries: 3,
    retryDelay: () => 1000,
});

// 维护待处理请求:key为请求唯一标识,value为对应AbortController
const pendingRequests = new Map<string, AbortController>();

// 生成请求唯一标识(method+url确保唯一性)
const generateRequestKey = (config: AxiosRequestConfig): string => {
    return `${config.method?.toUpperCase() || 'GET'}:${config.url}`;
};

// 请求拦截器
axiosClient.interceptors.request.use((config) => {
    const store = useConfigStore();
    const { loadingCount } = storeToRefs(store);
    const { selectedEnvironment } = storeToRefs(store);
    
    config.baseURL = `${EnvironmentEnum[selectedEnvironment.value]}/`;

    // 创建AbortController并绑定到请求
    const controller = new AbortController();
    config.signal = controller.signal;

    // 记录待处理请求
    const requestKey = generateRequestKey(config);
    pendingRequests.set(requestKey, controller);

    // 增加请求计数
    loadingCount.value += 1;

    return config;
});

// 响应拦截器
axiosClient.interceptors.response.use(
    (response) => {
        const store = useConfigStore();
        const { loadingCount } = storeToRefs(store);
        const requestKey = generateRequestKey(response.config);
        
        // 移除已完成的请求记录
        pendingRequests.delete(requestKey);

        // 减少请求计数(确保不出现负数)
        loadingCount.value = Math.max(0, loadingCount.value - 1);

        return response;
    },
    (error: AxiosError) => {
        const store = useConfigStore();
        const { loadingCount } = storeToRefs(store);
        
        // 取消所有待处理请求
        pendingRequests.forEach(controller => controller.abort());
        pendingRequests.clear();

        // 重置请求计数
        loadingCount.value = 0;

        // 错误提示
        const errorMsg = error.code === 'ERR_NETWORK' 
            ? error.message 
            : JSON.stringify(error.response?.data || error.message);
        notify(errorMsg, 'danger');

        // 必须返回reject,否则调用处无法捕获错误
        return Promise.reject(error);
    }
);

export default axiosClient;

3. 组件中使用(无需修改原有请求逻辑)

只需确保组件内的search函数添加async关键字:

const search = async () => {
    const data = ref();
    try {
        let result = await call1();
        if(result && result.status === 200) {
            data.value = await call2();
        }
    } catch (err) {
        // 捕获请求取消或其他错误
        console.error('请求异常:', err);
    }
};

三、方案2:请求队列化(串行执行)

如果业务允许请求串行执行,可通过队列方式让请求依次执行,前一个请求失败则终止后续请求,同时天然避免加载状态异常。

1. 在Axios实例中添加队列方法

// 在axiosClient.ts末尾添加
let requestQueue: Promise<any> = Promise.resolve();

// 封装队列请求方法
axiosClient.queueRequest = async <T>(config: AxiosRequestConfig): Promise<T> => {
    requestQueue = requestQueue.then(async () => {
        try {
            return await axiosClient(config);
        } catch (err) {
            // 出错后清空队列,后续请求不再执行
            requestQueue = Promise.resolve();
            throw err;
        }
    });
    return requestQueue;
};

2. 组件中使用队列请求

const search = async () => {
    const data = ref();
    try {
        // 用queueRequest替代原请求方法
        let result = await axiosClient.queueRequest({ url: '/api/call1', method: 'GET' });
        if(result && result.status === 200) {
            data.value = await axiosClient.queueRequest({ url: '/api/call2', method: 'GET' });
        }
    } catch (err) {
        console.error('请求中断:', err);
    }
};

方案选择建议

  • 方案1:适合需要并发请求、但要求异常时快速终止所有待处理请求的场景;
  • 方案2:适合业务逻辑本身要求请求串行执行的场景,实现更简单,无需处理并发状态。

内容的提问来源于stack exchange,提问作者BondAddict

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 13:02:23