关于定时器的使用及页面切换时定时器无法清除的问题解决办法

一、定时器的区别:

1、定时器分类:

JS有两种定时器分别是setTimeout()setInterval(),这两个区别就是setTimeout()是一次性的定时器,而setInterval()是循环的定时器。

(1)setTimeout()

只执行一次,在指定的毫秒数之后,就会立即停止

(2)setInterval()

不管语句是否执行完成,不管是否执行错误,到下一次指定的毫秒数后就会立即执行

(3)举例:

比如想调用一个接口,需要1s定时刷新一次,但是接口2s才返回数据,使用setInterval()的话就会出现问题,在上次接口还没有返回结果的情况下,又重新调用了接口,这样交互效果很不好,影响性能;所以在开发中,在遇到接口需要定时的情况,我都会使用setTimeout(),配合clearTimeout使用,需要注意的是:a. 不能把setTimeout写成死循环; b. 注意清除定时器的情况;c. 页面切换清除定时器的情况,这个是重点,也是下面需要讲到的问题

2、setInterval()的使用场景:

(1)可以使用在动画效果上,比如闪烁效果
(2)其他不需要异步及调用接口的方法 都可以使用(个人的总结,可能不太对)
(3)如果想停止,可以使用clearInterval()或关闭窗口

3、setTimeout()的使用场景

(1)定时器执行一次的情况下 可以使用,这种场景很少见,这里就不举例了
(2)调用的接口需要定时查询的情况,这种场景很常见,比如天气查询,定时10秒刷新一次,在开发中经常用到

4、定时器遇到的问题(以下是我在做项目中遇到的问题及自己的一点想法)

(1)用setTimeout 实现定时查询 一般用于执行的方法中调用后台接口的情况,等接口请求完后,再执行下一次的接口调用; 使用setInterval的话,一般用于不调用后台接口的情况
(2)要是用setInterval用来调用后端接口,可能会有问题,比如定时2s刷新,接口可能会3s才返回接口,还没等这次返回结果去刷新页面,就又去重新查询数据了,可能会造成数据混乱
(3)在使用setTimeout的时候,需要注意的问题就是不要写成死循环;再就是记得清除定时器,清除定时器用clearTimeout 方法
(4)在使用react的时候,会遇到路由切换的情况,有时候会出现定时器清除不掉的情况
比如在A页面中是用了定时器setTimeout,但是没有清除定时器,那么在访问B页面的时候,A页面的定时器还会执行,请求的接口也仍然会继续执行,所以要清除一下,在A页面切换到B页面的时候,会执行组件卸载的方法(componentWillUnmount),在该方法中调用清除定时器的方法(clearTimeout);

有时候可能会失败,比如刚巧在执行componentWillUnmount的时候,恰好代码也执行到定时器执行的调用后台接口的方法中了,还在请求接口,在请求成功到执行下一次定时器的时候,恰好componentWillUnmount方法把之前的定时器清除了,这样虽然清除了上次的定时器,但是那个方法中又再次使用了定时器,这样的话,就会造成清不掉的问题(这个大家可能会遇到这个问题)
(5)上述问题的React示例代码及运行截图
测试页面1的代码:

import React, { Component } from 'react';
import { getRealtimeValueByCodeList } from '../../SVGManagementNew/services/index'; // 引用的查询实时值的接口

export default class TestPage1 extends Component {
    constructor(props) {
        super(props);

        this.state = {
            refreshTime: 500 // 500毫秒
        };

        this.timeout = null;
    }

    componentDidMount() {
        this.getRealValueFn();
    }

    componentWillUnmount() {
        clearTimeout(this.timeout);
    }

    // 获取实时值
    getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FCALT2DPU2A')
            .then((res) => {
                console.log(res);

                this.timeout = setTimeout(() => {
                    console.log('page1定时查询');

                    this.getRealValueFn();
                }, this.state.refreshTime);
            })
            .catch((e) => {
                console.log(e);
            });
    };

    render() {
        return 
TestPage1
; } }

测试页面2的代码:
import React, { Component } from ‘react’;
import { getRealtimeValueByCodeList } from ‘…/…/SVGManagementNew/services/index’; // 引用的查询实时值的接口

export default class TestPage2 extends Component {
    constructor(props) {
        super(props);

        this.state = {
            refreshTime: 500 // 500毫秒
        };

        this.timeout = null;
    }

    componentDidMount() {
        this.getRealValueFn();
    }

    componentWillUnmount() {
        clearTimeout(this.timeout);
    }

    // 获取实时值
    getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FDRJ4_IDCS00116_R2')
            .then((res) => {
                console.log(res);

                this.timeout = setTimeout(() => {
                    console.log('page2定时查询');

                    this.getRealValueFn();
                }, this.state.refreshTime);
            })
            .catch((e) => {
                console.log(e);
            });
    };

    render() {
        return 
TestPage1
; } }

测试页面3的代码
import React, { useState, useRef, useEffect } from ‘react’;
import PropTypes from ‘prop-types’;
import { getRealtimeValueByCodeList } from ‘…/…/SVGManagementNew/services/index’; // 引用的查询实时值的接口

function TestPage3(props) {
    const [refreshTime, setRefreshTime] = useState(500);
    const timeout = useRef();

    const getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FDDM1_TEST_CTM')
            .then((res) => {
                console.log(res);
                timeout.current = setTimeout(() => {
                    console.log('page3定时查询');
                    getRealValueFn();
                }, refreshTime);
            })
            .catch((e) => {
                console.log(e);
            });
    };

    useEffect(() => {
        getRealValueFn();

        return () => {
            clearTimeout(timeout.current);
        };
    }, []);

    return 
TestPage3
; } TestPage3.propTypes = {}; export default TestPage3;

配置三个路由,然后来回切换这三个页面,就会出现定时器清除不掉的情况,三个页面的定时器就会一直在刷新,这样就会造成资源上的浪费及占用内容,要是页面很多、定时刷新的接口很多、接口很耗费资源的话,那么这种情况就会影响整个程序的运行,有可能还会引起系统崩溃
关于定时器的使用及页面切换时定时器无法清除的问题解决办法_第1张图片
上图就是我在来回切换三个测试页面的时候,出现上述说的问题,可以自己把上述的代码试验下

5、解决上述遇到的第四个问题,即使用setTimeout页面切换时定时器无法清除的问题

a. 解决思路:
(1)从问题的根本出发,问题的根本就是在页面切换的时候,因为定时器缓存的问题,在切换到其他的页面后,无法清除,还会继续在缓存中继续执行;
(2)那么从这个思路上去想,在从A页面(有定时器)跳转到B页面的时候,A页面的定时器没有清除掉,那么是否是可以在A页面的定时器方法那定义个变量,然后在全局定义个变量,然后通过这两个变量来判断是否不执行定时器呢;
(3)根据这个思路去想,那么是否可以根据路由去判断,比如在访问到A页面的时候,在A页面的代码中记录下A页面的路由到一个参数中(该参数在定时器执行的时候,是一块保存到缓存中的,在切换路由的时候,并不会清除,这个是一个存在缓存中的变量),然后在路由切换的时候,监听路由的变化,记录最新访问的路由,保存到window对象中(因为window是全局的,在任何地方都可以使用);
(4)那么这两个变量都有了,然后根据这两个变量判断,如果相等的话,那说明正好是这个页面,可以执行定时器,如果不相等,那么当前访问的页面不是这个页面,那么定时器就不执行了

b. 实现步骤:
(1)在外部定义一个监控方法,可以在main.js中 也可以在index.js中,也可以在路由切换中的文件中,把下面的代码加入进去,注意的是location的获取,有的js文件中可能获取不到

	window.onhashchange = () => {
        console.log('监听路由变化');
        // 获取当前变化的路由
        window.curPathName = this.props.location.pathname;
    };

下图是我自己添加的位置:
关于定时器的使用及页面切换时定时器无法清除的问题解决办法_第2张图片
(2)在使用定时器的文件中,设置每个文件使用时访问的路由地址

关于定时器的使用及页面切换时定时器无法清除的问题解决办法_第3张图片
(3)判断是否加载定时器
关于定时器的使用及页面切换时定时器无法清除的问题解决办法_第4张图片
c.完整的示例代码,可以正常清除定时器的
测试页面1的代码:

import React, { Component } from 'react';
import { getRealtimeValueByCodeList } from '../../SVGManagementNew/services/index'; // 引用的查询实时值的接口

export default class TestPage1 extends Component {
    constructor(props) {
        super(props);

        this.state = {
            refreshTime: 500, // 500毫秒
            curPageUrl: '' // 当前页面的访问地址
        };

        this.timeout = null;
    }

    componentDidMount() {
        const urlParams = window.location.href;
        this.setState({
            curPageUrl: urlParams
        });

        this.getRealValueFn();
    }

    componentWillUnmount() {
        clearTimeout(this.timeout);
    }

    // 获取实时值
    getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FCALT2DPU2A')
            .then((res) => {
                console.log(res);
                if (this.state.curPageUrl.indexOf(window.curPathName) >= 0 || !window.curPathName) {
                    this.timeout = setTimeout(() => {
                        console.log('page1定时查询');

                        this.getRealValueFn();
                    }, this.state.refreshTime);
                }
            })
            .catch((e) => {
                console.log(e);
            });
    };

    render() {
        return 
TestPage1
; } }

测试页面2的代码:

import React, { Component } from 'react';
import { getRealtimeValueByCodeList } from '../../SVGManagementNew/services/index'; // 引用的查询实时值的接口

export default class TestPage2 extends Component {
    constructor(props) {
        super(props);

        this.state = {
            refreshTime: 500, // 500毫秒
            curPageUrl: '' // 当前页面的访问地址
        };

        this.timeout = null;
    }

    componentDidMount() {
        const urlParams = window.location.href;
        this.setState({
            curPageUrl: urlParams
        });

        this.getRealValueFn();
    }

    componentWillUnmount() {
        clearTimeout(this.timeout);
    }

    // 获取实时值
    getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FDRJ4_IDCS00116_R2')
            .then((res) => {
                console.log(res);
                if (this.state.curPageUrl.indexOf(window.curPathName) >= 0 || !window.curPathName) {
                    this.timeout = setTimeout(() => {
                        console.log('page2定时查询');

                        this.getRealValueFn();
                    }, this.state.refreshTime);
                }
            })
            .catch((e) => {
                console.log(e);
            });
    };

    render() {
        return 
TestPage1
; } }

测试页面3的代码:

import React, { useState, useRef, useEffect } from 'react';
import PropTypes from 'prop-types';
import { getRealtimeValueByCodeList } from '../../SVGManagementNew/services/index'; // 引用的查询实时值的接口

function TestPage3(props) {
    const [refreshTime, setRefreshTime] = useState(500);
    const [curPageUrl, setCurPageUrl] = useState(window.location.href);
    const timeout = useRef();

    const getRealValueFn = () => {
        getRealtimeValueByCodeList('CF159FDDM1_TEST_CTM')
            .then((res) => {
                console.log(res);
                if (this.state.curPageUrl.indexOf(window.curPathName) >= 0 || !window.curPathName) {
                    timeout.current = setTimeout(() => {
                        console.log('page3定时查询');
                        getRealValueFn();
                    }, refreshTime);
                }
            })
            .catch((e) => {
                console.log(e);
            });
    };

    useEffect(() => {
        console.log(curPageUrl);

        getRealValueFn();

        return () => {
            clearTimeout(timeout.current);
        };
    }, []);

    return 
TestPage3
; } TestPage3.propTypes = {}; export default TestPage3;

总结:

以上就是针对定时器的用法和使用中遇到的问题的处理办法,如果有其他的实现思路可以在评论区留言

你可能感兴趣的:(React,前端学习笔记,jquery/js,react,js,前端框架,前端)