Commit 4288aeaf authored by Anthony Fok's avatar Anthony Fok

New upstream version 1.2.0

parent c930be37
sudo: false
language: go
sudo: false
go:
- 1.9.x
- 1.10.x
- "1.11.x"
- tip
os:
- linux
- osx
matrix:
allow_failures:
- go: tip
before_script:
- go get -u github.com/golang/lint/golint
fast_finish: true
script:
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_script:
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
- test -z "$(golint ./... | tee /dev/stderr)"
- go vet ./...
- env GO111MODULE=on go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)
os:
- linux
- osx
notifications:
email: false
- bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
......@@ -6,22 +6,7 @@
[![codecov](https://codecov.io/gh/bep/debounce/branch/master/graph/badge.svg)](https://codecov.io/gh/bep/debounce)
[![Release](https://img.shields.io/github/release/bep/debounce.svg?style=flat-square)](https://github.com/bep/debounce/releases/latest)
## Why?
This may seem like a fairly narrow library, so why not copy-and-paste it when needed? Sure -- but this is, however, slightly more usable than [left-pad](https://www.npmjs.com/package/left-pad), and as I move my client code into the [GopherJS](https://github.com/gopherjs/gopherjs) world, a [debounce](https://davidwalsh.name/javascript-debounce-function) function is a must-have.
This library works, but if you find any issue or a potential improvement, please create an issue or a pull request!
## Use
This package provides a debouncer func. The most typical use case would be the user
typing a text into a form; the UI needs an update, but let's wait for a break.
`New` returns a debounced function and a channel that can be closed to signal a stop of the goroutine. The function will, as long as it continues to be invoked, not be triggered. The function will be called after it stops being called for the given duration. Note that a stop signal means a full stop of the debouncer; there is no concept of flushing future invocations.
**Note:** The created debounced function can be invoked with different functions, if needed, the last one will win.
An example:
## Example
```go
func ExampleNew() {
......@@ -31,7 +16,7 @@ func ExampleNew() {
atomic.AddUint64(&counter, 1)
}
debounced, finish, done := debounce.New(100 * time.Millisecond)
debounced := debounce.New(100 * time.Millisecond)
for i := 0; i < 3; i++ {
for j := 0; j < 10; j++ {
......@@ -41,10 +26,6 @@ func ExampleNew() {
time.Sleep(200 * time.Millisecond)
}
close(finish)
<-done
c := int(atomic.LoadUint64(&counter))
fmt.Println("Counter is", c)
......@@ -52,10 +33,3 @@ func ExampleNew() {
}
```
## Tests
To run the tests, you need to install `Leaktest`:
```bash
go get -u github.com/fortytw2/leaktest
```
\ No newline at end of file
// Copyright © 2016 Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>.
// Copyright © 2019 Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.
......@@ -9,63 +9,35 @@
package debounce
import (
"sync"
"time"
)
// New returns a debounced function and two channels:
// 1. A quit channel that can be closed to signal a stop
// 2. A done channel that signals when the debouncer is completed
// of the goroutine.
// The function will, as long as it continues to be invoked, not be triggered.
// The function will be called after it stops being called for the given duration.
// The created debounced function can be invoked with different functions, if needed,
// New returns a debounced function that takes another functions as its argument.
// This function will be called when the debounced function stops being called
// for the given duration.
// The debounced function can be invoked with different functions, if needed,
// the last one will win.
// Also note that a stop signal means a full stop of the debouncer; there is no
// concept of flushing future invocations.
func New(d time.Duration) (func(f func()), chan struct{}, chan struct{}) {
in, out, quit := debounceChan(d)
done := make(chan struct{})
func New(after time.Duration) func(f func()) {
d := &debouncer{after: after}
go func() {
for {
select {
case f := <-out:
f()
case <-quit:
close(out)
close(in)
close(done)
return
}
}
}()
debounce := func(f func()) {
in <- f
return func(f func()) {
d.add(f)
}
return debounce, quit, done
}
func debounceChan(interval time.Duration) (in, out chan func(), quit chan struct{}) {
in = make(chan func(), 1)
out = make(chan func())
quit = make(chan struct{})
type debouncer struct {
mu sync.Mutex
after time.Duration
timer *time.Timer
}
go func() {
var f func() = func() {}
for {
select {
case f = <-in:
case <-time.After(interval):
out <- f
<-in
// new interval
case <-quit:
return
}
}
}()
func (d *debouncer) add(f func()) {
d.mu.Lock()
defer d.mu.Unlock()
return
if d.timer != nil {
d.timer.Stop()
}
d.timer = time.AfterFunc(d.after, f)
}
......@@ -8,12 +8,9 @@ import (
"time"
"github.com/bep/debounce"
"github.com/fortytw2/leaktest"
)
func TestDebounce(t *testing.T) {
defer leaktest.Check(t)()
var (
counter1 uint64
counter2 uint64
......@@ -31,7 +28,7 @@ func TestDebounce(t *testing.T) {
atomic.AddUint64(&counter2, 2)
}
debounced, shutdown, done := debounce.New(100 * time.Millisecond)
debounced := debounce.New(100 * time.Millisecond)
for i := 0; i < 3; i++ {
for j := 0; j < 10; j++ {
......@@ -52,10 +49,6 @@ func TestDebounce(t *testing.T) {
time.Sleep(200 * time.Millisecond)
}
close(shutdown)
<-done
c1 := int(atomic.LoadUint64(&counter1))
c2 := int(atomic.LoadUint64(&counter2))
if c1 != 3 {
......@@ -66,65 +59,53 @@ func TestDebounce(t *testing.T) {
}
}
func TestDebounceInParallel(t *testing.T) {
defer leaktest.Check(t)()
var counter uint64
f := func() {
atomic.AddUint64(&counter, 1)
}
func TestDebounceConcurrentAdd(t *testing.T) {
var wg sync.WaitGroup
debounced, shutdown, done := debounce.New(100 * time.Millisecond)
var flag uint64
var wg sync.WaitGroup
debounced := debounce.New(100 * time.Millisecond)
for i := 0; i < 20; i++ {
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
debouncedInner, shutdown, done := debounce.New(100 * time.Millisecond)
for j := 0; j < 10; j++ {
debouncedInner(f)
debounced(f)
}
time.Sleep(150 * time.Millisecond)
close(shutdown)
<-done
debounced(func() {
atomic.CompareAndSwapUint64(&flag, 0, 1)
})
}()
}
wg.Wait()
close(shutdown)
<-done
c := int(atomic.LoadUint64(&counter))
if c != 21 {
t.Error("Expected count 21, was", c)
time.Sleep(500 * time.Millisecond)
c := int(atomic.LoadUint64(&flag))
if c != 1 {
t.Error("Flag not set")
}
}
func TestDebounceCloseEarly(t *testing.T) {
defer leaktest.Check(t)()
// Issue #1
func TestDebounceDelayed(t *testing.T) {
var counter uint64
var (
counter1 uint64
)
f := func() {
atomic.AddUint64(&counter, 1)
f1 := func() {
atomic.AddUint64(&counter1, 1)
}
debounced, finish, done := debounce.New(100 * time.Millisecond)
debounced := debounce.New(100 * time.Millisecond)
debounced(f)
time.Sleep(110 * time.Millisecond)
close(finish)
debounced(f1)
<-done
time.Sleep(200 * time.Millisecond)
c := int(atomic.LoadUint64(&counter))
if c != 0 {
t.Error("Expected count 0, was", c)
c1 := int(atomic.LoadUint64(&counter1))
if c1 != 1 {
t.Error("Expected count 1, was", c1)
}
}
......@@ -136,14 +117,12 @@ func BenchmarkDebounce(b *testing.B) {
atomic.AddUint64(&counter, 1)
}
debounced, finish, done := debounce.New(100 * time.Millisecond)
debounced := debounce.New(100 * time.Millisecond)
b.ResetTimer()
for i := 0; i < b.N; i++ {
debounced(f)
}
close(finish)
<-done
c := int(atomic.LoadUint64(&counter))
if c != 0 {
......@@ -158,7 +137,7 @@ func ExampleNew() {
atomic.AddUint64(&counter, 1)
}
debounced, finish, done := debounce.New(100 * time.Millisecond)
debounced := debounce.New(100 * time.Millisecond)
for i := 0; i < 3; i++ {
for j := 0; j < 10; j++ {
......@@ -168,10 +147,6 @@ func ExampleNew() {
time.Sleep(200 * time.Millisecond)
}
close(finish)
<-done
c := int(atomic.LoadUint64(&counter))
fmt.Println("Counter is", c)
......
module github.com/bep/debounce
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment