JS中定時器的使用及頁面切換時定時器無法清除問題的解決辦法
一、定時器的區(qū)別:
1、定時器分類:
JS有兩種定時器分別是setTimeout()和setInterval(),這兩個區(qū)別就是setTimeout()是一次性的定時器,而setInterval()是循環(huán)的定時器。
(1)setTimeout()
只執(zhí)行一次,在指定的毫秒數(shù)之后,就會立即停止
(2)setInterval()
不管語句是否執(zhí)行完成,不管是否執(zhí)行錯誤,到下一次指定的毫秒數(shù)后就會立即執(zhí)行
(3)舉例:
比如想調(diào)用一個接口,需要1s定時刷新一次,但是接口2s才返回數(shù)據(jù),使用setInterval()的話就會出現(xiàn)問題,在上次接口還沒有返回結(jié)果的情況下,又重新調(diào)用了接口,這樣交互效果很不好,影響性能;所以在開發(fā)中,在遇到接口需要定時的情況,我都會使用setTimeout(),配合clearTimeout使用,需要注意的是:a. 不能把setTimeout寫成死循環(huán); b. 注意清除定時器的情況;c. 頁面切換清除定時器的情況,這個是重點,也是下面需要講到的問題
2、setInterval()的使用場景:
(1)可以使用在動畫效果上,比如閃爍效果
(2)其他不需要異步及調(diào)用接口的方法 都可以使用(個人的總結(jié),可能不太對)
(3)如果想停止,可以使用clearInterval()或關(guān)閉窗口
3、setTimeout()的使用場景
(1)定時器執(zhí)行一次的情況下 可以使用,這種場景很少見,這里就不舉例了
(2)調(diào)用的接口需要定時查詢的情況,這種場景很常見,比如天氣查詢,定時10秒刷新一次,在開發(fā)中經(jīng)常用到
4、定時器遇到的問題(以下是我在做項目中遇到的問題及自己的一點想法)
(1)用setTimeout 實現(xiàn)定時查詢 一般用于執(zhí)行的方法中調(diào)用后臺接口的情況,等接口請求完后,再執(zhí)行下一次的接口調(diào)用; 使用setInterval的話,一般用于不調(diào)用后臺接口的情況
(2)要是用setInterval用來調(diào)用后端接口,可能會有問題,比如定時2s刷新,接口可能會3s才返回接口,還沒等這次返回結(jié)果去刷新頁面,就又去重新查詢數(shù)據(jù)了,可能會造成數(shù)據(jù)混亂
(3)在使用setTimeout的時候,需要注意的問題就是不要寫成死循環(huán);再就是記得清除定時器,清除定時器用clearTimeout 方法
(4)在使用react的時候,會遇到路由切換的情況,有時候會出現(xiàn)定時器清除不掉的情況
比如在A頁面中是用了定時器setTimeout,但是沒有清除定時器,那么在訪問B頁面的時候,A頁面的定時器還會執(zhí)行,請求的接口也仍然會繼續(xù)執(zhí)行,所以要清除一下,在A頁面切換到B頁面的時候,會執(zhí)行組件卸載的方法(componentWillUnmount),在該方法中調(diào)用清除定時器的方法(clearTimeout);
有時候可能會失敗,比如剛巧在執(zhí)行componentWillUnmount的時候,恰好代碼也執(zhí)行到定時器執(zhí)行的調(diào)用后臺接口的方法中了,還在請求接口,在請求成功到執(zhí)行下一次定時器的時候,恰好componentWillUnmount方法把之前的定時器清除了,這樣雖然清除了上次的定時器,但是那個方法中又再次使用了定時器,這樣的話,就會造成清不掉的問題(這個大家可能會遇到這個問題)
(5)上述問題的React示例代碼及運(yùn)行截圖
測試頁面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 <div>TestPage1</div>; } }
測試頁面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 <div>TestPage1</div>; } }
測試頁面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 <div>TestPage3</div>; } TestPage3.propTypes = {}; export default TestPage3;
配置三個路由,然后來回切換這三個頁面,就會出現(xiàn)定時器清除不掉的情況,三個頁面的定時器就會一直在刷新,這樣就會造成資源上的浪費(fèi)及占用內(nèi)容,要是頁面很多、定時刷新的接口很多、接口很耗費(fèi)資源的話,那么這種情況就會影響整個程序的運(yùn)行,有可能還會引起系統(tǒng)崩潰
上圖就是我在來回切換三個測試頁面的時候,出現(xiàn)上述說的問題,可以自己把上述的代碼試驗下
5、解決上述遇到的第四個問題,即使用setTimeout頁面切換時定時器無法清除的問題
a. 解決思路:
(1)從問題的根本出發(fā),問題的根本就是在頁面切換的時候,因為定時器緩存的問題,在切換到其他的頁面后,無法清除,還會繼續(xù)在緩存中繼續(xù)執(zhí)行;
(2)那么從這個思路上去想,在從A頁面(有定時器)跳轉(zhuǎn)到B頁面的時候,A頁面的定時器沒有清除掉,那么是否是可以在A頁面的定時器方法那定義個變量,然后在全局定義個變量,然后通過這兩個變量來判斷是否不執(zhí)行定時器呢;
(3)根據(jù)這個思路去想,那么是否可以根據(jù)路由去判斷,比如在訪問到A頁面的時候,在A頁面的代碼中記錄下A頁面的路由到一個參數(shù)中(該參數(shù)在定時器執(zhí)行的時候,是一塊保存到緩存中的,在切換路由的時候,并不會清除,這個是一個存在緩存中的變量),然后在路由切換的時候,監(jiān)聽路由的變化,記錄最新訪問的路由,保存到window對象中(因為window是全局的,在任何地方都可以使用);
(4)那么這兩個變量都有了,然后根據(jù)這兩個變量判斷,如果相等的話,那說明正好是這個頁面,可以執(zhí)行定時器,如果不相等,那么當(dāng)前訪問的頁面不是這個頁面,那么定時器就不執(zhí)行了
b. 實現(xiàn)步驟:
(1)在外部定義一個監(jiān)控方法,可以在main.js中 也可以在index.js中,也可以在路由切換中的文件中,把下面的代碼加入進(jìn)去,注意的是location的獲取,有的js文件中可能獲取不到
window.onhashchange = () => { console.log('監(jiān)聽路由變化'); // 獲取當(dāng)前變化的路由 window.curPathName = this.props.location.pathname; };
下圖是我自己添加的位置:
(2)在使用定時器的文件中,設(shè)置每個文件使用時訪問的路由地址
(3)判斷是否加載定時器
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: '' // 當(dāng)前頁面的訪問地址 }; 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 <div>TestPage1</div>; } }
測試頁面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: '' // 當(dāng)前頁面的訪問地址 }; 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 <div>TestPage1</div>; } }
測試頁面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 <div>TestPage3</div>; } TestPage3.propTypes = {}; export default TestPage3;
總結(jié):
到此這篇關(guān)于JS中定時器的使用及頁面切換時定時器無法清除問題的解決辦法的文章就介紹到這了,更多相關(guān)JS定時器無法清除解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript獲取元素文本內(nèi)容的通用函數(shù)
獲取元素文本內(nèi)容的通用函數(shù),思路很好值得參考。2009-12-12使用Webpack壓縮與轉(zhuǎn)譯JavaScript代碼的操作方法
在Web開發(fā)中,代碼的性能和加載時間是用戶體驗的重要組成部分,為此,將JavaScript代碼壓縮和優(yōu)化是發(fā)布前一個必不可少的步驟,所以本文給大家介紹了如何使用Webpack壓縮與轉(zhuǎn)譯JavaScript代碼,需要的朋友可以參考下2024-05-05關(guān)于ckeditor在bootstrap中modal中彈框無法輸入的解決方法
今天小編就為大家分享一篇關(guān)于ckeditor在bootstrap中modal中彈框無法輸入的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09javascript根據(jù)時間生成m位隨機(jī)數(shù)最大13位
javascript根據(jù)時間生成m位隨機(jī)數(shù),最大13位隨機(jī)數(shù),并且不能保證首位不為0,實現(xiàn)代碼如下,需要的朋友可以參考下2014-10-10