-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathlogic.go
More file actions
151 lines (123 loc) · 2.68 KB
/
logic.go
File metadata and controls
151 lines (123 loc) · 2.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package toolbelt
import (
"context"
"fmt"
"sync"
"time"
)
type CtxErrFunc func(ctx context.Context) error
// Throttle will only allow the function to be called once every d duration.
func Throttle(d time.Duration, fn CtxErrFunc) CtxErrFunc {
shouldWait := false
mu := &sync.RWMutex{}
checkShoulWait := func() bool {
mu.RLock()
defer mu.RUnlock()
return shouldWait
}
return func(ctx context.Context) error {
if checkShoulWait() {
return nil
}
mu.Lock()
defer mu.Unlock()
shouldWait = true
go func() {
<-time.After(d)
shouldWait = false
}()
if err := fn(ctx); err != nil {
return fmt.Errorf("throttled function failed: %w", err)
}
return nil
}
}
// Debounce will only call the function after d duration has passed since the last call.
func Debounce(d time.Duration, fn CtxErrFunc) CtxErrFunc {
var t *time.Timer
mu := &sync.RWMutex{}
return func(ctx context.Context) error {
mu.Lock()
defer mu.Unlock()
if t != nil && !t.Stop() {
<-t.C
}
t = time.AfterFunc(d, func() {
if err := fn(ctx); err != nil {
fmt.Printf("debounced function failed: %v\n", err)
}
})
return nil
}
}
func CallNTimesWithDelay(d time.Duration, n int, fn CtxErrFunc) CtxErrFunc {
return func(ctx context.Context) error {
called := 0
for {
shouldCall := false
if n < 0 {
shouldCall = true
} else if called < n {
shouldCall = true
}
if !shouldCall {
break
}
if err := fn(ctx); err != nil {
return fmt.Errorf("call n times with delay failed: %w", err)
}
called++
<-time.After(d)
}
return nil
}
}
// DebounceWithMaxWait creates a debounced function that waits for a quiet period
// before executing, but guarantees execution within a maximum wait time.
func DebounceWithMaxWait(waitTime time.Duration, maxWaitTime time.Duration, fn func(context.Context) error) func(context.Context) error {
var (
mu sync.Mutex
timer *time.Timer
maxTimer *time.Timer
latestCtx context.Context
firstCallAt time.Time
)
execute := func() {
mu.Lock()
ctx := latestCtx
mu.Unlock()
if ctx != nil {
fn(ctx)
}
mu.Lock()
timer = nil
maxTimer = nil
latestCtx = nil
firstCallAt = time.Time{}
mu.Unlock()
}
return func(ctx context.Context) error {
mu.Lock()
defer mu.Unlock()
latestCtx = ctx
// First call in this burst
if firstCallAt.IsZero() {
firstCallAt = time.Now()
// Start max wait timer
maxTimer = time.AfterFunc(maxWaitTime, execute)
}
// Reset debounce timer
if timer != nil {
timer.Stop()
}
timer = time.AfterFunc(waitTime, func() {
mu.Lock()
if maxTimer != nil {
maxTimer.Stop()
}
mu.Unlock()
execute()
})
return nil
}
}