在项目开发中,一个页面经常会有多个接口请求。正常情况下,接口请求正常,然后各自处理自己的回调函数即可。但是如果因为网络或者其他原因,这些请求触发了错误回调,就可能会有多个message提示同时出现的情况。
解决思路
先分析一下问题,我们其实只能更合理地处理这种类似问题,但是没有很完美的处理方式。下面有几个思路我先列出来,然后看看有没有什么问题。
- 合并错误提示:我们可以将同一时间(例如在很短的时间间隔内)发生的多个错误合并成一个错误提示,告诉用户有多个错误发生。
- 使用队列和防抖:将错误提示放入一个队列中,然后使用防抖(debounce)或节流(throttle)技术来延迟显示,并在延迟时间内合并相同的错误或不同的错误,最后统一显示一个错误提示。
- 优先级提示:如果错误有优先级,比如有些错误更重要,那么我们可以只显示最高优先级的错误。
- 避免重复错误:有时候多个错误可能是同一个原因导致的,我们可以通过错误信息去重,避免显示重复的错误。
分析:
四点!其实无非两个思路。即合并和优先级。
对于第一个解决办法,合并 。它其实是在设定好的一定时间内将所有的错误信息收集起来,然后集中提示出来。这种方式的问题是,如果同一个页面的请求有些需要耗时特别长,超过了设定的时间。那么合并就会将其漏掉,如果一味地加时长,那么可能错误提示还出来,就因为长时间白屏或者数据为空,用户已经离开了,造成的用户体验非常差。
对于第二种,优先级。首先它同样会有设定时间段的问题,即上面合并的缺点。因为要显示高优先级错误就势必要先收集有哪些并发的请求,不过同一个优先级怎么办呢?这种时候也许会按照最后一个来提示?那么其他同样高优先级的怎么办?没有看到,无法处理,只能一个个处理完了才能接着下一个(当然是用户需要处理的情况下,很多时候其实不需要用户来处理)
尝试处理
合并处理
定义一个收集错误信息队列的类:
kotlin
// 错误管理器(单例模式)
class ErrorManager {
constructor() {
this.errorQueue = []
this.debounceTimer = null
this.debounceDelay = 500 // 500ms内错误合并
}
pushError(error) {
this.errorQueue.push(error)
if (!this.debounceTimer) {
this.debounceTimer = setTimeout(() => this.flushErrors(), this.debounceDelay)
}
}
flushErrors() {
if (this.errorQueue.length === 0) return
// 合并错误信息
const uniqueErrors = [...new Set(this.errorQueue)]
const message = uniqueErrors.length > 1
? `${uniqueErrors.length}项操作失败,请检查`
: uniqueErrors[0]
// 显示UI提示(示例使用antd)
notification.error({
message: '操作失败',
description: message,
duration: 3
})
// 重置状态
this.errorQueue = []
this.debounceTimer = null
}
}
再来是全局处理:
vbscript
// 使用示例(在请求拦截器中)
const errorManager = new ErrorManager()
// 这里仅仅用了error回调来举例,其实我们有些业务上的错误也可以这样做的,只不过放在response里处理即可
axios.interceptors.response.use(
response => response,
error => {
const errorMsg = error.response?.data?.message || '请求失败'
errorManager.pushError(errorMsg)
return Promise.reject(error)
}
)
这里做了一个优化,因为这些错误信息很可能是相同的,所以用Set()做了一下去重。避免相同的信息反复呈现。
优先级
优先级这里不想做的那么复杂,反正既然就算有优先级也可能会有相同优先级的一起出现,不如化整为零,都默认一样的优先级。当然这里如果有特殊要求,最好按照需求来。
最终处理起来很简单了:
scss
let activeError = null
function showError(msg) {
if (activeError) activeError.destroy() // 关闭前一个提示
activeError = message.error(msg)
}
ElementUI的话就是这样:
javascript
import { Message } from 'element-ui'
let currentMessage = null
const messageToast = (options) => {
if (messageUse) {
currentMessage.close()
}
currentMessage = Message(options)
}
export default messageToast
核心原则:减少视觉干扰,提升信息密度(合并信息) 。
至于UI怎么显示的话,看具体需求,我倒是认为可以做一些区分,比如errorQueue里面只有一条错误信息,就用Message来提示,简便,但是如果有多条,则可以用Notification侧边通知这种。根据UI设计的统一规范来即可。
结论
可以将合并操作和关闭旧提示两个操作合二为一,达到一个相对合理的处理多个弹窗同时出现的应对方式