前言:当我们在写项目发起请求时,我们可能会习惯于在请求过程中数据还未返回时展示一个loading组件,请求结束后就隐藏loading,渲染数据。这是没有问题的,不过目前网速越来越快,请求都十分迅速,经常会导致loading组件刚呈现数据就已经返回了,导致loading组件闪烁,十分影响用户体验,但若是不用loading若部分用户网速不行,也会导致请求的长时间白屏问题,同样影响用户体验。所以就需要一个两全其美的方法。
1.Promise.all() 解决
我们可以利用定时器在固定时间后将一个promise状态改为成功,然后将这个promise与axios请求放入Promise.all()中,那么只有当两个promise都成功时才渲染数据,从而实现当请求过快时也能展示固定时间的loading效果。
const reolvePromise = (reolveTime: number): Promise<unknown> => { // 指定时间后返回状态成功的promise
return new Promise((resolve) => {
setTimeout(() => {
resolve(`在${reolveTime}ms后返回成功Promise`)
}, reolveTime)
})
}
loading.value = true // 发送请求时开启
Promise.all([ajaxRequest(), reolvePromise(200)])
.then((res) => { // Promise.all执行结果返回的数组顺序是按传入顺序决定的
console.log(res[0])
})
.catch((err) => {
console.log(err)
})
.finally(() => {
loading.value = false
})
虽然使用Promise.all()看起来很美好,但网速快请求迅速也要等待loading展示结束,这就有些强行展示loading的感觉了。
2.Promise.race() 解决
另外一种新方式是利用Promise.race()。同样利用定时器,在固定时间后将一个promise状态改为失败,然后将这个promise与axios请求放入Promise.race()中,这意味着在固定时间内请求返回就直接渲染数据,而超出固定时间才展示loading效果。
const rejectPromise = (rejectTime: number): Promise<unknown> => { // 指定时间后返回状态失败的promise
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error(`在${rejectTime}ms后返回失败Promise`))
}, rejectTime)
})
}
const request = ajaxRequest() // 记录请求的状态
Promise.race([request, rejectPromise(1000)])
.then((res) => {
// 成功意味着请求在固定时间内返回
})
.catch((err) => { // 超时,整体变成onrejected,展示loading并继续等待返回
loading.value = true
console.log(err.message)
request
.then((res) => {
// 请求终于成功了,渲染数据
})
.finally(() => {
loading.value = false
})
})
虽然使用Promise.race()看起来更美好了,但依然存在问题,如果规定1000ms是超时时间,若请求在1100ms时返回,则loading只会展示100ms,依然会出现闪烁现象,所以如果用此方法解决需要估算每个请求的平均花费时间,不要让超时时间十分靠近请求的平均花费时间。
3.Promise.all() 搭配 Promise.race() 终极解决方式
终极解决方式是将 Promise.all() 和 Promise.race() 搭配使用。先利用Promise.race()约束请求在超时时间内返回时就直接渲染,否则就固定展示一段时间的loading动画再渲染数据。即请求如果没有在 500ms 内返回则固定展示 1500ms 的loading,这样才十分完美。
function reqData (): void {
const axiosRequest = ajaxRequest() // 记录请求的状态
Promise.race([axiosRequest, rejectPromise(500)])
.then((res) => {
// 成功意味着请求在固定时间内返回
})
.catch((err) => { // 超时,整体变成onrejected,展示loading
loading.value = true
console.log(err.message)
Promise.all([axiosRequest, reolvePromise(1500)])
.then((res) => { // Promise.all执行结果返回的数组顺序是按传入顺序决定的
console.log(res[0])
})
.catch((err) => {
console.log(err)
})
.finally(() => {
loading.value = false
})
})
}
好啦,到这里就说完啦,点个赞啊靓仔~