mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
feat: add Debounce function
This commit is contained in:
@@ -28,7 +28,8 @@ import (
|
||||
- [Before](#Before)
|
||||
- [CurryFn](#CurryFn)
|
||||
- [Compose](#Compose)
|
||||
- [Debounced](#Debounced)
|
||||
- [Debounce](#Debounce)
|
||||
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||
- [Delay](#Delay)
|
||||
- [Schedule](#Schedule)
|
||||
- [Pipeline](#Pipeline)
|
||||
@@ -193,9 +194,58 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounce">Debounce</span>
|
||||
|
||||
<p>创建一个函数的去抖动版本。该去抖动函数仅在上次调用后的指定延迟时间过去之后才会调用原始函数。支持取消去抖动。</p>
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
```go
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||
```
|
||||
|
||||
<b>示例:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
debouncedFn()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
debouncedFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounced">Debounced</span>
|
||||
|
||||
<p>创建一个debounced函数,该函数延迟调用fn直到自上次调用debounced函数后等待持续时间过去。</p>
|
||||
<p>创建一个函数的去抖动版本。</p>
|
||||
|
||||
> ⚠️ 本函数已弃用. 使用 `Debounce` 代替.
|
||||
|
||||
<b>函数签名:</b>
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@ import (
|
||||
- [Before](#Before)
|
||||
- [CurryFn](#CurryFn)
|
||||
- [Compose](#Compose)
|
||||
- [Debounced](#Debounced)
|
||||
- [Debounce](#Debounce)
|
||||
- [Debounced<sup>deprecated</sup>](#Debounced)
|
||||
- [Delay](#Delay)
|
||||
- [Schedule](#Schedule)
|
||||
- [Pipeline](#Pipeline)
|
||||
@@ -191,11 +192,59 @@ func main() {
|
||||
// ABCDE
|
||||
}
|
||||
```
|
||||
### <span id="Debounce">Debounce</span>
|
||||
|
||||
<p>Creates a debounced version of the provided function. The debounced function will only invoke the original function after the specified delay has passed since the last time it was invoked. It also supports canceling the debounce.</p>
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func())
|
||||
```
|
||||
|
||||
<b>Example:</b>
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/duke-git/lancet/v2/function"
|
||||
)
|
||||
|
||||
func main() {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := function.Debounce(fn, 500*time.Millisecond)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
debouncedFn()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
debouncedFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
```
|
||||
|
||||
### <span id="Debounced">Debounced</span>
|
||||
|
||||
<p>Creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.</p>
|
||||
|
||||
> ⚠️ This function is deprecated. use `Debounce` instead.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```go
|
||||
|
||||
@@ -7,6 +7,7 @@ package function
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -84,22 +85,44 @@ func Delay(delay time.Duration, fn any, args ...any) {
|
||||
}
|
||||
|
||||
// Debounced creates a debounced function that delays invoking fn until after wait duration have elapsed since the last time the debounced function was invoked.
|
||||
// Deprecated: Use Debounce function instead.
|
||||
// Play: https://go.dev/play/p/absuEGB_GN7
|
||||
func Debounced(fn func(), duration time.Duration) func() {
|
||||
// Catch programming error while constructing the closure
|
||||
mustBeFunction(fn)
|
||||
func Debounced(fn func(), delay time.Duration) func() {
|
||||
debouncedFn, _ := Debounce(fn, delay)
|
||||
return debouncedFn
|
||||
}
|
||||
|
||||
timer := time.NewTimer(duration)
|
||||
timer.Stop()
|
||||
// Debounce creates a debounced version of the provided function.
|
||||
// Play: todo
|
||||
func Debounce(fn func(), delay time.Duration) (debouncedFn func(), cancelFn func()) {
|
||||
var (
|
||||
timer *time.Timer
|
||||
mu sync.Mutex
|
||||
)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
<-timer.C
|
||||
go fn()
|
||||
debouncedFn = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
return func() { timer.Reset(duration) }
|
||||
timer = time.AfterFunc(delay, func() {
|
||||
fn()
|
||||
})
|
||||
}
|
||||
|
||||
cancelFn = func() {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if timer != nil {
|
||||
timer.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
return debouncedFn, cancelFn
|
||||
}
|
||||
|
||||
// Schedule invoke function every duration time, util close the returned bool channel.
|
||||
|
||||
@@ -79,6 +79,32 @@ func ExampleDelay() {
|
||||
// hello
|
||||
}
|
||||
|
||||
func ExampleDebounce() {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
debouncedFn()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
debouncedFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println(callCount)
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// 2
|
||||
}
|
||||
|
||||
func ExampleDebounced() {
|
||||
count := 0
|
||||
add := func() {
|
||||
|
||||
@@ -125,6 +125,80 @@ func TestDebounced(t *testing.T) {
|
||||
assert.Equal(2, count)
|
||||
}
|
||||
|
||||
func TestDebounce(t *testing.T) {
|
||||
assert := internal.NewAssert(t, "TestDebounce")
|
||||
|
||||
t.Run("Single call", func(t *testing.T) {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
|
||||
debouncedFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(1, callCount)
|
||||
})
|
||||
|
||||
t.Run("Multiple calls within debounce interval", func(t *testing.T) {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := Debounce(fn, 1*time.Second)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
go func(index int) {
|
||||
time.Sleep(time.Duration(index) * 200 * time.Millisecond)
|
||||
debouncedFn()
|
||||
}(i)
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
assert.Equal(1, callCount)
|
||||
})
|
||||
|
||||
t.Run("Immediate consecutive calls", func(t *testing.T) {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, _ := Debounce(fn, 500*time.Millisecond)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
debouncedFn()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(1, callCount)
|
||||
})
|
||||
|
||||
t.Run("Cancel calls", func(t *testing.T) {
|
||||
callCount := 0
|
||||
fn := func() {
|
||||
callCount++
|
||||
}
|
||||
|
||||
debouncedFn, cancelFn := Debounce(fn, 500*time.Millisecond)
|
||||
|
||||
debouncedFn()
|
||||
|
||||
cancelFn()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
assert.Equal(0, callCount)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func TestSchedule(t *testing.T) {
|
||||
// assert := internal.NewAssert(t, "TestSchedule")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user