我的用例是:当用户停止在文本框中输入时,我将调用一个函数
process_input()
,我不能对每个字符都调用它,我只需要在输入完成后调用它。
所以思路是:使用辅助函数来节流
process_input()
,如果在一段时间内重复调用,之前的调用将被丢弃,一段时间后只会触发最后一次调用。
我想这样使用它:
func process_input() {
fmt.Println(`process_input() called`)
}
func main() {
fn := buffer.Timed(time.Second, process_input)
fn() // shouldn't be called
fn() // may be called or not
time.Sleep(time.Second)
fn() // shouldn't be called
fn() // will be called
time.Sleep(time.Hour) // block it from exiting
}
我的实现:
package buffer
import (
"sync/atomic"
"time"
)
func Timed(dur time.Duration, fn func()) func() {
kill := make(chan bool)
isScheduled := atomic.Bool{}
return func() {
if isScheduled.Load() { // if there is previous job scheduled
kill <- true // kill the previous, sometimes this line causes deadlock.
}
// start the next
isScheduled.Store(true)
go func() {
defer isScheduled.Store(false)
select {
case <-time.After(dur):
go fn()
case <-kill:
}
}()
}
}
有时会死机崩溃
kill <- true
,如何解决?
您在处理同步时犯了许多逻辑错误,但是您可以通过减少并发性并仅使用单个
time.Timer
来为您触发该函数来避免大多数复杂情况。
您的目标(至少如上面代码中所示)是在延迟后触发该函数,并在每次新调用时重置该延迟如果该函数尚未被调用。您可以使用单个
time.Timer
和互斥锁来保护该字段:https://go.dev/play/p/PaQRhKo44Bl
func NewDebouncer(dur time.Duration) func(fn func()) {
d := &debouncer{
dur: dur,
}
return func(fn func()) {
d.reset(fn)
}
}
type debouncer struct {
mu sync.Mutex
dur time.Duration
delay *time.Timer
}
func (d *debouncer) reset(fn func()) {
d.mu.Lock()
defer d.mu.Unlock()
if d.delay != nil {
d.delay.Stop() {
}
d.delay = time.AfterFunc(d.dur, fn)
}