Skip to content

Commit

Permalink
feat: 新增 debounce 和 throttle
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Jun 10, 2019
1 parent d990a7e commit 2148d25
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/debounce.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { debounce } from './debounce'
import { times } from './times'
import { wait } from './wait'

test('表现正常', async () => {
const fn: (x: number, y: number) => number = jest.fn().mockImplementation((x: number, y: number) => x + y)

const debouncedFn = debounce(fn, 500)

times(10000, () => debouncedFn(1, 2))

await wait(550)

expect(fn).toBeCalledTimes(1)

times(10000, () => debouncedFn(1, 2))

await wait(550)

expect(fn).toBeCalledTimes(2)
})
36 changes: 36 additions & 0 deletions src/debounce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { AnyFunction } from './enhanceType'

/**
* 创建一个去抖函数,将触发频繁的事件合并成一次执行。
*
* 该函数被调用后,计时 `wait` 毫秒后调用 `fn` 函数。
* 若在 `wait` 毫秒内该函数再次被调用,则重新开始计时。
*
* 一个应用场景:监听输入框的 `input` 事件发起网络请求。
*
* @param fn 要去抖的函数
* @param wait 需要等待的毫秒数
* @returns 返回去抖后的函数
* @example
* ```ts
* document.querySelector('#input').oninput = debounce(
* e => {
* console.log(e.target.value)
* },
* 500,
* )
* ```
*/
export function debounce<T extends AnyFunction>(fn: T, wait: number): (...args: Parameters<T>) => void {
let timer: NodeJS.Timeout | null = null

return function (this: any) {
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(
() => fn.apply(this, arguments as any),
wait,
)
}
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './base64'
export * from './castArray'
export * from './chunk'
export * from './clamp'
export * from './debounce'
export * from './Disposer'
export * from './EasyStorage'
export * from './EasyValidator'
Expand Down Expand Up @@ -45,6 +46,7 @@ export * from './sequential'
export * from './shuffle'
export * from './startsWith'
export * from './sum'
export * from './throttle'
export * from './times'
export * from './unique'
export * from './URI'
Expand Down
26 changes: 26 additions & 0 deletions src/throttle.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { throttle } from './throttle'
import { wait } from './wait'

test('表现正常', async () => {
const fn: (x: number, y: number) => number = jest.fn().mockImplementation((x: number, y: number) => x + y)

const throttledFn = throttle(fn, 500)

expect(fn).toBeCalledTimes(0)

throttledFn(1, 2)

expect(fn).toBeCalledTimes(1)

throttledFn(1, 2)

expect(fn).toBeCalledTimes(1)

await wait(400)

expect(fn).toBeCalledTimes(1)

await wait(200)

expect(fn).toBeCalledTimes(2)
})
69 changes: 69 additions & 0 deletions src/throttle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { AnyFunction } from './enhanceType'

/**
* 创建一个节流函数,给函数设置固定的执行速率。
*
* - 该函数首次被调用时,会立即调用 `fn` 函数,并记录首次调用时间。
* - 该函数第二次被调用时:
* - 如果该次调用时间在首次调用时间的 `wait` 区间内,`timer = setTimeout(操作, 时间差)`;
* - 该函数再次被调用时:
* - 如果该次调用时间在首次调用时间的 `wait` 区间内,什么都不做;
* - 否则,清除首次调用时间和计时器,回到第一步。
* - 否则,清除首次调用时间,回到第一步。
*
* 一个应用场景:监听窗口的 `resize` 事件响应相关操作。
*
* @param fn 要节流的函数
* @param wait 需要等待的毫秒数
* @returns 返回节流后的函数
* @example
* ```ts
* window.addEventListener(
* 'resize',
* throttle(
* () => console.log('窗口大小改变后的操作'),
* 1000,
* ),
* )
* ```
*/
export function throttle<T extends AnyFunction>(fn: T, wait: number): (...args: Parameters<T>) => void {
let timer: NodeJS.Timeout | null = null
let lastTime: number | null = null

return function (this: any) {
const currentTime = new Date().getTime()

const action = () => {
lastTime = currentTime
fn.apply(this, arguments as any)
}

// 首次调用
if (!lastTime) {
action()
}

// 第二次调用
else if (!timer) {
if (currentTime < lastTime + wait) {
timer = setTimeout(
() => {
fn.apply(this, arguments as any)
},
lastTime + wait - currentTime,
)
} else {
action()
}
}

// 再次调用
else {
if (currentTime > lastTime + wait) {
timer = null
action()
}
}
}
}

0 comments on commit 2148d25

Please sign in to comment.