什么是防抖節(jié)流
為了防止某個特定的函數(shù)(尤其是內(nèi)部有異步操作)在未得到結(jié)果前被重復(fù)執(zhí)行,或者想要用戶輸入、頁面滾動等行為所對應(yīng)的函數(shù)不那么頻繁的被觸發(fā)而使用的限制操作稱為防抖節(jié)流。
let isPending=false;//--
async function throttle(){
if(isPending) return;//--
isPending=true;//--
try{//--
await new Promise(resolve=>{
setTimeout(_=>{
resolve();//或者reject
},3000)
})
}catch(err){
//do nothing
};
isPending=false;//--
}
但是這樣仍然比較麻煩,因?yàn)槲覀兇蟛糠智闆r下寫了一堆額外的代碼只是為了讓開關(guān)正常歸位。
不過最近在寫egg+vue+ts服務(wù)端渲染的時(shí)候使用了基于類的api開發(fā)方式,雖然感覺vuex結(jié)合ts并不太甜但是感覺ts還是真香,vue 的class寫法雖然讓我們喪失了mapState這樣非常方便的操作但同時(shí)解鎖了新的黑科技--decorator(裝飾器),下面我們使用裝飾器來實(shí)現(xiàn)一個非侵入式的節(jié)流功能;
/**
* @param {number} deylay當(dāng)函數(shù)執(zhí)行完后再次執(zhí)行的時(shí)間間隔
*/
function Throttle(deylay: number = 0): Function {
let isPending: boolean = false;
return (target: any, name: string, descriptor: any) => {
var oldValue = descriptor.value;
//重寫函數(shù)
descriptor.value = async function (...args: any[]) {
if (isPending) return;
isPending = true;
try {
await oldValue.apply(this, args);
} catch (err) {
//do nothing
};
if (!deylay) return isPending = false;
setTimeout(() => {
isPending = false;
}, deylay);
};
return descriptor;
};
};
//測試
class Test {
@Throttle()
public async fn():Promise<void>{
console.log("fn 執(zhí)行了");
await new Promise(resolve => {
setTimeout(()=> {
resolve();
}, 3000);
});
};
};
const test = new Test();
setInterval(test.fn,50);
利用類似的思路我們同樣可以實(shí)現(xiàn)請求耗時(shí)、日志等很多功能:
//經(jīng)測試會有毫秒級的誤差
function TimeCost(target, name, descriptor) {
var oldValue = descriptor.value;
descriptor.value = async function (...args) {
const timeStart = new Date().getTime()
try {
await oldValue.apply(this, args);
} catch (err) {
//
};
console.log(`function:[${name}] costs: ${new Date().getTime() - timeStart}ms`);
};
return descriptor;
};