Skip to content

Commit

Permalink
Merge pull request vnteamopen#21 from vnteamopen/ft/add-done-function
Browse files Browse the repository at this point in the history
Support <-debouncer.Done()
  • Loading branch information
huyvohcmc authored May 12, 2022
2 parents 146473d + c53e25b commit ab8103a
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 27 deletions.
37 changes: 25 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ import (
)

func main() {
wait := 5 * time.Second
debouncer := godebouncer.New(wait).WithTriggered(func() {
debouncer := godebouncer.New(5 * time.Second).WithTriggered(func() {
fmt.Println("Trigger") // Triggered func will be called after 5 seconds from last SendSignal().
})

Expand All @@ -45,10 +44,10 @@ func main() {

fmt.Println("Action 2")
debouncer.SendSignal()

// After 5 seconds, the trigger will be called.
// Previous `SendSignal()` will be ignored to trigger the triggered function.

time.Sleep(10 * time.Second)
<-debouncer.Done()
}
```

Expand All @@ -59,8 +58,7 @@ func main() {
Allows defining actions before calling SendSignal(). They are synchronous.

```go
wait := 10 * time.Second
debouncer := godebouncer.New(wait).WithTriggered(func() {
debouncer := godebouncer.New(10 * time.Second).WithTriggered(func() {
fmt.Println("Trigger") // Triggered func will be called after 10 seconds from last SendSignal().
})

Expand All @@ -76,8 +74,7 @@ debouncer.Do(func() {
Allows cancelling the timer from the last function SendSignal(). The scheduled triggered function is cancelled and doesn't invoke.

```go
wait := 10 * time.Second
debouncer := godebouncer.New(wait).WithTriggered(func() {
debouncer := godebouncer.New(10 * time.Second).WithTriggered(func() {
fmt.Println("Trigger") // Triggered func will be called after 10 seconds from last SendSignal().
})

Expand All @@ -90,8 +87,7 @@ debouncer.Cancel() // No triggered function is called
Allows replacing triggered function.

```go
wait := 10 * time.Second
debouncer := godebouncer.New(wait).WithTriggered(func() {
debouncer := godebouncer.New(10 * time.Second).WithTriggered(func() {
fmt.Println("Trigger 1") // Triggered func will be called after 10 seconds from last SendSignal().
})

Expand All @@ -108,8 +104,7 @@ debouncer.UpdateTriggeredFunc(func() {
Allows replacing the waiting time duration. You need to call a SendSignal() again to trigger a new timer with a new waiting time duration.

```go
wait := 10 * time.Second
debouncer := godebouncer.New(wait).WithTriggered(func() {
debouncer := godebouncer.New(10 * time.Second).WithTriggered(func() {
fmt.Println("Trigger") // Triggered func will be called after 10 seconds from last SendSignal().
})

Expand All @@ -118,6 +113,24 @@ debouncer.SendSignal()
// Output: "Trigger" after 20 seconds
```

## Let the caller knows when the triggered function has been invoked

Allows the caller of godebouncer knows when the triggered function is done invoking to synchronize execution across goroutines.

```go
debouncer := godebouncer.New(1 * time.Second).WithTriggered(func() {
fmt.Println("Fetching...")
time.Sleep(2 * time.Second)
fmt.Println("Done")
})

debouncer.SendSignal()

<-debouncer.Done() // The current goroutine will wait until the triggered func finish its execution.

fmt.Println("After done")
```

# License

MIT
Expand Down
10 changes: 9 additions & 1 deletion debouncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ type Debouncer struct {
timer *time.Timer
triggeredFunc func()
mu sync.Mutex
done chan struct{}
}

// New creates a new instance of debouncer. Each instance of debouncer works independent, concurrency with different wait duration.
func New(duration time.Duration) *Debouncer {
return &Debouncer{timeDuration: duration, triggeredFunc: func() {}}
return &Debouncer{timeDuration: duration, triggeredFunc: func() {}, done: make(chan struct{})}
}

// WithTriggered attached a triggered function to debouncer instance and return the same instance of debouncer to use.
Expand All @@ -31,6 +32,7 @@ func (d *Debouncer) SendSignal() {
d.Cancel()
d.timer = time.AfterFunc(d.timeDuration, func() {
d.triggeredFunc()
d.done <- struct{}{}
})
}

Expand All @@ -45,6 +47,7 @@ func (d *Debouncer) Cancel() {
if d.timer != nil {
d.timer.Stop()
}
d.done = make(chan struct{})
}

// UpdateTriggeredFunc replaces triggered function.
Expand All @@ -56,3 +59,8 @@ func (d *Debouncer) UpdateTriggeredFunc(newTriggeredFunc func()) {
func (d *Debouncer) UpdateTimeDuration(newTimeDuration time.Duration) {
d.timeDuration = newTimeDuration
}

// Done returns a receive-only channel to notify the caller when the triggered func has been executed.
func (d *Debouncer) Done() <-chan struct{} {
return d.done
}
49 changes: 35 additions & 14 deletions debouncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ func Example() {

fmt.Println("Action 2")
debouncer.SendSignal()

// After 5 seconds, the trigger will be called.
//Previous `SendSignal()` will be ignore to trigger the triggered function.

time.Sleep(10 * time.Second)
<-debouncer.Done()
}

func createIncrementCount(counter int) (*int, func()) {
Expand All @@ -49,7 +49,7 @@ func TestDebounceDoBeforeExpired(t *testing.T) {
fmt.Println("Action 2")
})

time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -65,13 +65,13 @@ func TestDebounceDoAfterExpired(t *testing.T) {
fmt.Println("Action 1")
})

time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

debouncer.Do(func() {
fmt.Println("Action 2")
})

time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -91,13 +91,13 @@ func TestDebounceMixed(t *testing.T) {
fmt.Println("Action 2")
})

time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

debouncer.Do(func() {
fmt.Println("Action 3")
})

time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -110,7 +110,8 @@ func TestDebounceWithoutTriggeredFunc(t *testing.T) {
debouncer.Do(func() {
fmt.Println("Action 1")
})
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

fmt.Println("debouncer.Do() finished successfully!")
}

Expand All @@ -120,7 +121,7 @@ func TestDebounceSendSignal(t *testing.T) {
expectedCounter := int(1)

debouncer.SendSignal()
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -138,7 +139,7 @@ func TestDebounceUpdateTriggeredFuncBeforeDuration(t *testing.T) {
debouncer.UpdateTriggeredFunc(func() {
*countPtr += 2
})
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -151,13 +152,13 @@ func TestDebounceUpdateTriggeredFuncAfterDuration(t *testing.T) {
expectedCounter := int(3)

debouncer.SendSignal()
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

debouncer.UpdateTriggeredFunc(func() {
*countPtr += 2
})
debouncer.SendSignal()
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand Down Expand Up @@ -187,7 +188,7 @@ func TestDebounceUpdateDuration(t *testing.T) {

debouncer.UpdateTimeDuration(200 * time.Millisecond)
debouncer.SendSignal()
time.Sleep(400 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand All @@ -203,7 +204,27 @@ func TestDebounceUpdateDurationAfterSendSignal(t *testing.T) {
time.Sleep(200 * time.Millisecond)

debouncer.UpdateTimeDuration(600 * time.Millisecond)
time.Sleep(300 * time.Millisecond)
<-debouncer.Done()

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
}
}

func TestDone(t *testing.T) {
countPtr, incrementCount := createIncrementCount(0)
debouncer := godebouncer.New(200 * time.Millisecond).WithTriggered(incrementCount)
expectedCounter := int(2)

fmt.Println("Action 1")
debouncer.SendSignal()
time.Sleep(400 * time.Millisecond)

fmt.Println("Action 2")
debouncer.SendSignal()
<-debouncer.Done()

fmt.Println(len(debouncer.Done()))

if *countPtr != expectedCounter {
t.Errorf("Expected count %d, was %d", expectedCounter, *countPtr)
Expand Down

0 comments on commit ab8103a

Please sign in to comment.