在前端开发中,批量请求可能会因为网络问题、服务器错误或权限问题而失败。如果每个失败的请求都弹出一个 Toast 提示,用户体验会很差。因此,我们需要确保在短时间内多个请求失败时,只显示一个 Toast 提示。
实现思路
- 维护一个全局状态,用于记录是否已经弹出 Toast。
- 使用节流函数(
throttle
)或定时器,确保短时间内多个请求失败时,仅触发一次 Toast。
- 支持批量请求的全局错误拦截,如拦截 Axios 的响应错误,或者在 Fetch API 中封装统一的错误处理。
具体实现
1. 使用全局状态记录 Toast
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import axios from "axios"; import { message } from "antd";
let toastShown = false;
function showGlobalToast(msg: string) { if (!toastShown) { toastShown = true; message.error(msg); setTimeout(() => { toastShown = false; }, 3000); } }
axios.interceptors.response.use( (response) => response, (error) => { showGlobalToast("请求失败,请稍后重试"); return Promise.reject(error); } );
async function fetchBatchData() { try { await Promise.all([ axios.get("/api/data1"), axios.get("/api/data2"), axios.get("/api/data3"), ]); } catch (error) { console.error("批量请求发生错误", error); } }
|
- 全局状态
toastShown
:用来记录当前是否已经有 Toast 被弹出,避免在多个请求失败时重复弹出提示。
showGlobalToast
方法:在 Toast 未显示的情况下调用 message.error
弹出提示,并设置一个 3 秒的定时器,在定时器到期后将 toastShown
重置为 false
,这样在下一次错误时可以重新弹出提示。
- Axios 拦截器:捕获所有响应错误,并调用
showGlobalToast
方法,确保全局只有一次 Toast 弹出。
2. 使用 throttle 限制 Toast 触发频率
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import axios from "axios"; import { message } from "antd"; import throttle from "lodash/throttle";
const showToast = throttle( (msg: string) => { message.error(msg); }, 3000, { leading: true, trailing: false } );
axios.interceptors.response.use( (response) => response, (error) => { showToast("请求失败,请稍后重试"); return Promise.reject(error); } );
async function fetchBatchData() { try { await Promise.all([ axios.get("/api/data1"), axios.get("/api/data2"), axios.get("/api/data3"), ]); } catch (error) { console.error("批量请求发生错误", error); } }
|
throttle
确保 showToast
在 3 秒内最多执行一次,即使多个请求失败,也只会弹出一个 Toast。
优化
如何只限制文案相同的 Toast
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const messageTextSet: string[] = [];
const showToast = (msg: string) => { if (!messageTextSet.includes(msg)) { messageTextSet.push(msg) message.error(msg); setTimeout(() => { const index = messageTextSet.findIndex((item) => item === msg); if (index !== -1) { messageTextSet.splice(index, 1); } }, 3000); } }
|
通过在 messageTextSet
中临时存储将要提示的文案,如果命中则忽略,未命中的话就存入 messageTextSet
,然后弹 Toast 提示,提示过后再从 messageTextSet
中删除刚刚提示的文案。