axios 中如何实现并发请求和取消请求?请提供代码示例
Axios 并发请求Axios 提供了 axios.all() 和 axios.spread() 方法来处理并发请求,同时也支持使用原生的 Promise.all()。1. 使用 Promise.all()(推荐)// 同时发送多个请求async function fetchMultipleData() { try { const [users, posts, comments] = await Promise.all([ axios.get('/api/users'), axios.get('/api/posts'), axios.get('/api/comments') ]); console.log('Users:', users.data); console.log('Posts:', posts.data); console.log('Comments:', comments.data); return { users: users.data, posts: posts.data, comments: comments.data }; } catch (error) { console.error('至少一个请求失败:', error); throw error; }}2. 使用 axios.all()(传统方式)axios.all([ axios.get('/api/users'), axios.get('/api/posts'), axios.get('/api/comments')]).then(axios.spread((users, posts, comments) => { // 所有请求都成功时执行 console.log('Users:', users.data); console.log('Posts:', posts.data); console.log('Comments:', comments.data);})).catch(error => { // 任一请求失败时执行 console.error('请求失败:', error);});3. 并发请求的错误处理async function fetchWithErrorHandling() { const requests = [ axios.get('/api/users'), axios.get('/api/posts'), axios.get('/api/comments') // 可能失败的请求 ]; // 使用 Promise.allSettled 等待所有请求完成 const results = await Promise.allSettled(requests); results.forEach((result, index) => { if (result.status === 'fulfilled') { console.log(`请求 ${index} 成功:`, result.value.data); } else { console.error(`请求 ${index} 失败:`, result.reason.message); } }); // 过滤出成功的结果 const successfulResults = results .filter(result => result.status === 'fulfilled') .map(result => result.value.data); return successfulResults;}4. 限制并发数量// 使用 p-limit 或自定义实现限制并发async function fetchWithConcurrencyLimit(urls, limit = 3) { const results = []; const executing = []; for (const [index, url] of urls.entries()) { const promise = axios.get(url).then(res => ({ index, data: res.data })); results.push(promise); if (urls.length >= limit) { executing.push(promise); if (executing.length >= limit) { await Promise.race(executing); executing.splice( executing.findIndex(p => p === promise), 1 ); } } } return Promise.all(results);}// 使用const urls = ['/api/data/1', '/api/data/2', '/api/data/3', '/api/data/4'];fetchWithConcurrencyLimit(urls, 2);Axios 取消请求1. 使用 AbortController(推荐,v0.22.0+)// 创建 AbortControllerconst controller = new AbortController();// 发送请求时传入 signalaxios.get('/api/data', { signal: controller.signal}).then(response => { console.log(response.data);}).catch(error => { if (axios.isCancel(error)) { console.log('请求已取消:', error.message); } else { console.error('请求失败:', error); }});// 取消请求controller.abort('用户取消操作');// 5秒后自动取消setTimeout(() => { controller.abort('请求超时');}, 5000);2. 在 React 组件中使用import { useEffect } from 'react';function UserList() { useEffect(() => { const controller = new AbortController(); const fetchUsers = async () => { try { const response = await axios.get('/api/users', { signal: controller.signal }); // 处理数据 } catch (error) { if (axios.isCancel(error)) { console.log('组件卸载,请求已取消'); } else { console.error('获取用户失败:', error); } } }; fetchUsers(); // 组件卸载时取消请求 return () => { controller.abort('组件卸载'); }; }, []); return <div>User List</div>;}3. 在 Vue 组件中使用<script setup>import { onMounted, onUnmounted } from 'vue';let controller;onMounted(() => { controller = new AbortController(); axios.get('/api/data', { signal: controller.signal }) .then(response => { console.log(response.data); }) .catch(error => { if (axios.isCancel(error)) { console.log('请求已取消'); } });});onUnmounted(() => { controller?.abort('组件卸载');});</script>4. 取消多个请求const controllers = new Map();// 发送请求时保存 controllerfunction fetchWithCancel(key, url) { // 取消之前的同名请求 if (controllers.has(key)) { controllers.get(key).abort('重复请求,取消前一个'); } const controller = new AbortController(); controllers.set(key, controller); return axios.get(url, { signal: controller.signal }) .finally(() => { controllers.delete(key); });}// 使用:搜索框防抖场景fetchWithCancel('search', '/api/search?q=keyword');5. 请求超时自动取消async function fetchWithTimeout(url, timeout = 5000) { const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(`请求超时 (${timeout}ms)`); }, timeout); try { const response = await axios.get(url, { signal: controller.signal }); clearTimeout(timeoutId); return response.data; } catch (error) { clearTimeout(timeoutId); throw error; }}6. 取消请求的工具函数封装class RequestManager { constructor() { this.controllers = new Map(); } // 发送请求 async request(key, config) { // 取消之前的同名请求 this.cancel(key); const controller = new AbortController(); this.controllers.set(key, controller); try { const response = await axios({ ...config, signal: controller.signal }); return response; } finally { this.controllers.delete(key); } } // 取消指定请求 cancel(key, message = '请求被取消') { if (this.controllers.has(key)) { this.controllers.get(key).abort(message); this.controllers.delete(key); } } // 取消所有请求 cancelAll(message = '所有请求被取消') { this.controllers.forEach(controller => { controller.abort(message); }); this.controllers.clear(); }}// 使用const requestManager = new RequestManager();// 发送请求requestManager.request('userList', { method: 'GET', url: '/api/users'});// 取消指定请求requestManager.cancel('userList');// 取消所有请求(如页面切换时)requestManager.cancelAll();最佳实践组件卸载时取消请求:避免内存泄漏和状态更新错误重复请求时取消前一个:搜索框、表单提交等场景设置合理的超时时间:防止请求挂起正确处理取消错误:区分取消错误和业务错误使用 AbortController:现代浏览器标准 API,兼容性好