长轮询的实现方式及优化

长轮询的实现方式及优化

Posted by Xshellv on 2020-10-23

最近在公司在做关于CRN的发布流程工具,其中不免涉及到发布状态的轮询,因此在这里记录了实现方式,主要涉及页面打开隐藏控制轮询的开启与关闭。此外还需要注意,若上一次请求未得到及时响应的话,需要等待上一次请求完成后再发送下一个请求,主要为防止网络请求过慢造成后续请求集体阻塞。

轮询创建

创建一个接受执行轮询操作的方法和轮询时间间隔:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export const createRefresher = (f, delay) => { // 创建一个接受执行轮询操作的方法和轮询时间间隔
let timer = null;
// 开启轮询的方法
const start = () => {
if (timer === undefined) {
timer = setInterval(f, delay);
} else {
clearInterval(timer);
timer = undefined;
timer = setInterval(f, delay);
}
};

// 关闭轮询的方法
const stop = () => {
if (timer) {
clearInterval(timer);
timer = undefined;
}
};

return { start, stop };
}

在类组件的构造方法中创建轮询

1
2
3
4
5
6
7
export default class PackagePublishDetail extends Component {
static contextType = CommonContext;
constructor() {
// 调用创建轮询的方法
this.refresher = createRefresher(this.queryPublishStatus, 3000);
}
}

开启与关闭

1
2
3
4
5
// ... 开启轮询
this.refresher.start()

// ... 关闭轮询
this.refresher.stop()

页面显隐控制轮询

为了节省开销,我们不可能每时每刻处于长轮询状态,尤其是多人发布时,很可能会导致服务器压力过大而崩溃。当用户打开页面查看并操作时,需要了解发布状态进入哪个环节,这时需要轮询状态,否则需要关闭轮询。

绑定和解绑visibilitychange事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
componentWillUnmount() {
document.removeEventListener('visibilitychange', this.handleVisibilitychange);
this.refresher.stop();
}

componentDidMount() {
document.addEventListener('visibilitychange', this.handleVisibilitychange);
}

handleVisibilitychange = () => {
if (document.hidden) {
this.refresher.stop();
} else {
this.queryPublishStatus();
}
};

进一步优化

对上述的handleVisibilitychange方法前加入轮询完成标志。

1
2
3
4
5
6
7
8
handleVisibilitychange = () => {
if (document.hidden) {
this.refresher.stop();
} else {
// 我这里用的this.allBack(),该方法会返回所有执行轮询的异步请求完成的一个标志。
this.allBack() && this.queryPublishStatus();
}
};