这个问题你一定见过:页面一进来打了十几个接口,其中好几个因为 401/500 挂了,然后 UI 像“放鞭炮”一样连弹十几个 Toast。

用户第一反应不是“哦有些接口失败了”,而是“这页面炸了”。所以更合理的体验是:短时间内同类错误合并提示,最多弹一次(或者合并成一个更清晰的提示)。

实现思路

  1. 维护一个全局状态,用于记录是否已经弹出 Toast。
  2. 使用节流函数throttle)或定时器,确保短时间内多个请求失败时,仅触发一次 Toast。
  3. 支持批量请求的全局错误拦截,如拦截 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";

// 定义一个全局状态,记录是否已经显示 Toast
let toastShown = false;

// 封装一个方法来显示 Toast,并设置定时器恢复状态
function showGlobalToast(msg: string) {
if (!toastShown) {
toastShown = true;
message.error(msg);
// 3秒后重置状态,允许下次错误时再次弹出 Toast
setTimeout(() => {
toastShown = false;
}, 3000);
}
}

// Axios 全局响应拦截器,捕获请求错误
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"; // 使用 Antd 的 Toast 组件
import throttle from "lodash/throttle";

// 1. 创建一个节流的 Toast 方法,确保 3 秒内只弹一次
const showToast = throttle(
(msg: string) => {
message.error(msg);
},
3000,
{ leading: true, trailing: false }
);

// 2. Axios 全局错误拦截
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
const messageTextSet = new Set();

const showToast = (msg: string) => {
if (!messageTextSet.has(msg)) {
messageTextSet.add(msg);
message.error(msg);
setTimeout(() => {
messageTextSet.delete(msg);
}, 3000);
}
}

// 其余代码省略

思路很简单:把“正在提示中的文案”放到一个集合里,命中就不弹;过一段时间再释放,允许后续再次提示。

实际项目里你还可以把 key 做得更精细一点,比如按 errorCode/status/接口域名 合并,而不是只按文案合并,这样提示会更准确。

Happy Coding!