# 基于事件循环的节流函数
在阅读此节之前,请你确保已经熟练的掌握 JavaScript 的事件循环机制
首先说明一下什么情况下有这样一个场景。
以下是一个 Vue 组件
<template>
<div>
{{ i }}
</div>
<button @click="doIncrease">增加计数</button>
</template>
<script setup>
import { ref } from 'vue'
const i = ref(0)
doIncrease() {
i.value++;
i.value++;
}
</script>
当我们点击的时候,i 肯定是变动了两次,但是 vue 是肯定只更新一次的(很明显两次 i 的变动是在一轮事件循环内,没有必要更新两次。)
所以,这就提出了一个要求,如何实现一个函数实现基于事件循环来节流呢?
实现这个函数,我们还是得有模有样的参考lodash
的throttle
方法,心中先有一个大概的实现框架:
export declare function eventLoopThrottle<T extends unknown[], R>(
fn: (...args: T) => R,
ctx?: unknown
): (...args: T) => R;
然后再思考一下,既然是基于事件循环,肯定会有一个标记,根据标记判断是否能够执行函数,然后在下一轮宏任务里面重置这个标记。
export function eventLoopThrottle<T extends unknown[], R>(
fn: (...args: T) => R,
ctx: unknown
): (...args: T) => R {
let isExecuted = false;
return function throttled(...args: T) {
// 如果已经执行过,不再执行
if (isExecuted) {
return;
}
// 执行函数,并且拿到结果
const response = fn.apply(ctx || this, args);
// 设置标记,同一轮事件循环内将无法再次执行
isExecuted = true;
setTimeout(() => {
// 在下一个宏任务里面清除已经执行的标记
isExecuted = false;
}, 0);
return response;
};
}