golang零内存分配的去抖动功能插件库debounce的使用
Golang零内存分配的去抖动功能插件库debounce的使用
简介
Debounce是一个简单、线程安全的去抖动库,用于Go语言。它会在最后一次调用后延迟指定时间才执行函数,非常适合用于限制频率、减少冗余操作以及在高频场景中优化性能。
特性
- 零内存分配:在后续的去抖动调用中不会分配内存
- 线程安全:可在多个goroutine中并发使用
- 通道支持:可以在
chan
上使用Chan函数 - 可配置延迟和限制:使用WithDelay和WithLimit选项设置自定义行为
- 零依赖:仅使用Go标准库构建
安装
go get github.com/floatdrop/debounce/v2
使用示例
基本用法
import (
"fmt"
"time"
"github.com/floatdrop/debounce/v2"
)
func main() {
// 创建一个200毫秒延迟的去抖动器
debouncer := debounce.New(debounce.WithDelay(200 * time.Millisecond))
// 快速连续调用多次
debouncer.Do(func() { fmt.Println("Hello") })
debouncer.Do(func() { fmt.Println("World") })
// 等待足够长时间让去抖动器执行
time.Sleep(time.Second)
// 输出: World
}
带通道的使用示例
import (
"fmt"
"time"
"github.com/floatdrop/debounce/v2"
)
func main() {
// 创建一个通道去抖动器,延迟500毫秒
ch := make(chan struct{})
debouncer := debounce.Chan(500 * time.Millisecond)
go func() {
for range debouncer(ch) {
fmt.Println("事件触发")
}
}()
// 快速发送多个事件
ch <- struct{}{}
ch <- struct{}{}
ch <- struct{}{}
// 等待足够长时间
time.Sleep(time.Second)
// 输出: 事件触发 (只输出一次)
}
带限制的使用示例
import (
"fmt"
"time"
"github.com/floatdrop/debounce/v2"
)
func main() {
// 创建一个去抖动器,延迟200毫秒,最多每100毫秒执行一次
debouncer := debounce.New(
debounce.WithDelay(200 * time.Millisecond),
debounce.WithLimit(100 * time.Millisecond),
)
start := time.Now()
// 快速连续调用多次
for i := 0; i < 10; i++ {
debouncer.Do(func() {
fmt.Printf("执行于: %v\n", time.Since(start))
})
time.Sleep(50 * time.Millisecond)
}
time.Sleep(time.Second)
}
性能基准测试
go test -bench=. -benchmem
示例输出:
goos: darwin
goarch: arm64
pkg: github.com/floatdrop/debounce/v2
cpu: Apple M3 Max
BenchmarkDebounce_Insert-14 4860848 234.0 ns/op 0 B/op 0 allocs/op
BenchmarkDebounce_Do-14 5188065 230.8 ns/op 0 B/op 0 allocs/op
PASS
ok github.com/floatdrop/debounce/v2 7.805s
贡献
欢迎贡献代码!请随时提交Pull Request。
许可证
该项目采用MIT许可证。
更多关于golang零内存分配的去抖动功能插件库debounce的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang零内存分配的去抖动功能插件库debounce的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 零内存分配的去抖动(Debounce)功能实现
去抖动(Debounce)是一种常见的技术,用于限制函数调用的频率,确保在快速连续触发时只执行一次。下面我将介绍如何在Go中实现零内存分配的去抖动功能。
基本概念
去抖动的主要应用场景包括:
- 按钮防重复点击
- 搜索框输入联想
- 窗口大小调整事件
- 滚动事件处理
零内存分配实现
以下是零内存分配的debounce实现示例:
package debounce
import (
"sync"
"time"
)
type Debouncer struct {
mu sync.Mutex
after time.Duration
timer *time.Timer
}
func New(after time.Duration) *Debouncer {
return &Debouncer{after: after}
}
func (d *Debouncer) Do(f func()) {
d.mu.Lock()
defer d.mu.Unlock()
if d.timer != nil {
d.timer.Stop()
}
d.timer = time.AfterFunc(d.after, f)
}
使用示例
package main
import (
"debounce"
"fmt"
"time"
)
func main() {
d := debounce.New(500 * time.Millisecond)
for i := 0; i < 10; i++ {
d.Do(func() {
fmt.Println("Executed after debounce")
})
time.Sleep(100 * time.Millisecond)
}
time.Sleep(1 * time.Second)
}
高级特性实现
如果需要更复杂的功能,比如立即执行、取消等,可以扩展实现:
type DebounceOptions struct {
Leading bool // 是否立即执行第一次调用
Trailing bool // 是否在延迟后执行最后一次调用
}
type AdvancedDebouncer struct {
mu sync.Mutex
after time.Duration
timer *time.Timer
pending bool
options DebounceOptions
lastFunc func()
}
func NewAdvanced(after time.Duration, options DebounceOptions) *AdvancedDebouncer {
return &AdvancedDebouncer{
after: after,
options: options,
}
}
func (d *AdvancedDebouncer) Do(f func()) {
d.mu.Lock()
defer d.mu.Unlock()
d.lastFunc = f
if d.timer != nil {
d.timer.Stop()
}
if d.options.Leading && !d.pending {
go d.lastFunc()
}
d.pending = true
d.timer = time.AfterFunc(d.after, func() {
d.mu.Lock()
defer d.mu.Unlock()
if d.options.Trailing && d.lastFunc != nil {
go d.lastFunc()
}
d.pending = false
d.lastFunc = nil
})
}
func (d *AdvancedDebouncer) Cancel() {
d.mu.Lock()
defer d.mu.Unlock()
if d.timer != nil {
d.timer.Stop()
}
d.pending = false
d.lastFunc = nil
}
性能优化
为了实现零内存分配,我们需要注意以下几点:
- 重用timer对象而不是每次创建新的
- 使用sync.Pool管理临时对象
- 避免闭包捕获外部变量导致内存分配
实际应用场景
// 搜索框输入联想
searchDebouncer := debounce.New(300 * time.Millisecond)
searchInput.OnChange(func(text string) {
searchDebouncer.Do(func() {
// 实际执行搜索
performSearch(text)
})
})
// 窗口大小调整
resizeDebouncer := debounce.New(100 * time.Millisecond)
window.OnResize(func() {
resizeDebouncer.Do(func() {
// 重新布局
relayout()
})
})
注意事项
- 确保在适当的时候调用Cancel(),比如组件卸载时
- 注意并发安全问题,多个goroutine调用Do()时需要同步
- 合理设置debounce时间间隔,太短达不到效果,太长影响用户体验
这种实现方式避免了频繁的内存分配,适合高性能要求的场景。