在項(xiàng)目開發(fā)中經(jīng)常會(huì)用到計(jì)時(shí)器,比如抽獎(jiǎng)活動(dòng)的倒計(jì)時(shí),或者輪循去請(qǐng)求接口等,通常我們都是用 setInterval 這個(gè)方法去實(shí)現(xiàn)計(jì)時(shí),但是這樣有一個(gè)缺點(diǎn),雖然能設(shè)置隔一段時(shí)間后不斷執(zhí)行,但是實(shí)際上只是將事件放消息隊(duì)列,真正執(zhí)行的時(shí)間并不確定,有可能上一個(gè)計(jì)時(shí)器任務(wù)沒執(zhí)行完又進(jìn)來一個(gè)計(jì)時(shí)器任務(wù),所以并不能保證能按照設(shè)定的時(shí)間去執(zhí)行,如果用 setTimeout 來模擬的話可以這樣
function runTimer() {
(function inner() {
let t = setTimeout(() => {
console.log('time count')
clearTimeout(t);
inner();
}, 1000);
})();
}
通過上面的方式可以在隔一秒就輸出一個(gè) time count,通過一個(gè)立即執(zhí)行方法不斷調(diào)用自身來實(shí)現(xiàn) setInterval 的效果,可以保證每次執(zhí)行的間隔時(shí)間都是一致的。
這種方法只是相對(duì)簡(jiǎn)單的,我們?cè)陧?xiàng)目開發(fā)過程中可能有不止一個(gè)計(jì)時(shí)器,我們希望對(duì)所有的計(jì)時(shí)器統(tǒng)一進(jìn)行處理,這時(shí)我們可以封裝一個(gè)類來管理這些計(jì)時(shí)器,它包含以下幾個(gè)內(nèi)容
-
timerList--存放計(jì)時(shí)器的數(shù)組 -
addTimer--往timerList添加計(jì)時(shí)器的方法,參數(shù)是一個(gè)對(duì)象,包含名稱,回調(diào)方法跟時(shí)間 -
runTimer--執(zhí)行某個(gè)計(jì)時(shí)器的方法,參數(shù)是計(jì)時(shí)器的名稱 -
clearTimer--清除某個(gè)計(jì)時(shí)器的方法,參數(shù)是計(jì)時(shí)器的名稱
具體實(shí)現(xiàn)如下:
// timer.js
class timer {
timerList = [];
addTimer(name, callback, time = 1000) {
this.timerList.push({
name,
callback,
time
});
this.runTimer(name);
}
runTimer(name) {
const _this = this;
(function inner() {
const task = _this.timerList.find((item) => {
return item.name === name;
});
if (!task) return;
task.t = setTimeout(() => {
task.callback();
clearTimeout(task.t);
inner();
}, task.time);
})();
}
clearTimer(name) {
const taskIndex = this.timerList.findIndex((item) => {
return item.name === name;
});
if (taskIndex !== -1) {
// 由于刪除該計(jì)時(shí)器時(shí)可能存在該計(jì)時(shí)器已經(jīng)入棧,所以要先清除掉,防止添加的時(shí)候重復(fù)計(jì)時(shí)
clearTimeout(this.timerList[taskIndex].t);
this.timerList.splice(taskIndex, 1);
}
}
}
export default new timer();
這里特別說明下每個(gè)計(jì)時(shí)器的方法都是在計(jì)時(shí)器的 t 屬性上定義的, 方便 clearTimer 方法中去清除掉該計(jì)時(shí)器,這是為了處理在特定場(chǎng)景下產(chǎn)生的錯(cuò)誤,比如我們?cè)趫?zhí)行一個(gè)計(jì)時(shí)器時(shí)會(huì)先判斷該計(jì)時(shí)器是否存在,如果存在就刪除該計(jì)時(shí)器,這里我們通過 name 值去判斷,如果只是單純把計(jì)時(shí)器從數(shù)組中移除的話,可能在刪除的時(shí)候它已經(jīng)進(jìn)入 setTimeout 了,這時(shí)再開啟一個(gè)同名的計(jì)時(shí)器的話,就會(huì)造成兩個(gè)相同的計(jì)時(shí)器同時(shí)執(zhí)行,所以才需要這么處理。
通過以上的方式封裝計(jì)時(shí)器,我們就可以在任何頁面進(jìn)行引用了
import timer from './timer.js';
timer.clearTimer('xxx'); // 先清除除計(jì)時(shí)器
timer.addTimer('xxx', () => {
console.log(123)
}, 1000)
如果想要每個(gè)計(jì)時(shí)器的命名都是唯一的話,還可以使用 Symbol 去定義計(jì)時(shí)器的名稱,比如可以新建一個(gè)專門存放計(jì)時(shí)器名稱的文件
// timeSymbol.js
export const statTimer = Symbol("statTimer");
export const endTimer = Symbol("endTimer");
然后通過以下方式引用
import timer from './timer.js';
import {statTimer} from './timeSymbol.js';
timer.clearTimer(statTimer); // 先清除除計(jì)時(shí)器
timer.addTimer(statTimer, () => {
console.log(123)
}, 1000)
這樣就能保證所有的計(jì)時(shí)器都是唯一的,不用擔(dān)心會(huì)有同名的計(jì)時(shí)器存在造成錯(cuò)誤。