0%

ajax相应时间过快,页面loading闪烁?

背景

现在绝大部分异步请求都有如下类似的套路代码

1
2
3
4
loading = true;
ajax().finally(() => {
loading = false;
});

都0202年了,高速的网络会导致loading出现闪烁情况

Promise.all的解决方案

假设我有两个ajax请求时间分别是50ms150ms,我现在希望不管是50ms还是150ms,loading动画都有一个比较完整的展示时间

这种情况只需要用一个延迟的Promise.resolve(),通过Promise.all方法去拉长响应时间

1
2
3
4
5
6
7
8
9
const delay = ms => new Promise((resolve, _) => setTimeout(resolve, ms));

loading = true;
Promise.all([ajaxPromise, delay(300)])
.then(handleSuccess)
.catch(handleError)
.finally(() => {
loading = false;
});

这种解决方案对于响应快的情况有点本末倒置的感觉

Promise.race的解决方案

现在我希望响应时间超过100ms的情况才展示loading动画

这种情况只需要用一个延迟的Promise.reject(),通过Promise.race方法去和ajax竞态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const timeout = ms =>
new Promise((_, reject) => setTimeout(() => reject(Symbol.for('timeout')), ms));

Promise.race([ajaxPromise, timeout(100)])
.then(handleSuccess)
.catch(err => {
if (Symbol.for('timeout') === err) {
loading = true;
return ajaxPromise
.then(handleSuccess)
.catch(handleError)
.finally(() => {
loading = false;
});
}
return handleError(err);
});

当我的响应时间为101ms的时候,闪烁还是无法避免的

Promise.all和Promise.race的解决方案

现在我希望响应时间小于100ms时不展示loading动画,大于100ms时展示300ms的loading动画时间

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
40
41
42
43
44
45
46
47
const timeout = ms =>
new Promise((_, reject) => setTimeout(() => reject(Symbol.for('timeout')), ms));

const delay = ms => new Promise((resolve, _) => setTimeout(resolve, ms));

const request = ({ config, target, timeoutTime = 100, delayTime = 300 }) => {
// 返回promise的ajax请求
const promise = axios(config);

const startLoading = target => {
if (!target) {
return;
}
// startLoading
};

const endLoading = () => {
// endLoading
};

const handleSuccess = data => {
// 兼容Promise.all和Promise.race不同的返回值
const response = Array.isArray(data) ? data[0] : data;
// 处理成功的情况
return Promise.resolve(response.data);
};

const handleError = ({ response }) => {
// 处理失败的情况
return Promise.reject(response);
};

return Promise.race([promise, timeout(timeoutTime)])
.then(handleSuccess)
.catch(err => {
if (Symbol.for('timeout') === err) {
startLoading(target);
return Promise.all([promise, delay(delayTime)])
.then(handleSuccess)
.catch(handleError)
.finally(() => {
endLoading();
});
}
return handleError(err);
});
};

timeoutTime和delayTime可以根据自己的网站调整