pax_global_header00006660000000000000000000000064132623457640014526gustar00rootroot0000000000000052 comment=844797fa1dd9ba969d71b62797ff19d1e49d4eac debounce-1.1.0/000077500000000000000000000000001326234576400133115ustar00rootroot00000000000000debounce-1.1.0/.gitignore000066400000000000000000000004371326234576400153050ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof cover.out nohup.out debounce-1.1.0/.travis.yml000066400000000000000000000007411326234576400154240ustar00rootroot00000000000000sudo: false language: go go: - 1.9.x - 1.10.x - tip matrix: allow_failures: - go: tip before_script: - go get -u github.com/golang/lint/golint 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 ./... after_success: - bash <(curl -s https://codecov.io/bash) os: - linux - osx notifications: email: false debounce-1.1.0/LICENSE000066400000000000000000000020771326234576400143240ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Bjørn Erik Pedersen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. debounce-1.1.0/README.md000066400000000000000000000043771326234576400146030ustar00rootroot00000000000000# Go Debounce [![Build Status](https://travis-ci.org/bep/debounce.svg)](https://travis-ci.org/bep/debounce) [![GoDoc](https://godoc.org/github.com/bep/debounce?status.svg)](https://godoc.org/github.com/bep/debounce) [![Go Report Card](https://goreportcard.com/badge/github.com/bep/debounce)](https://goreportcard.com/report/github.com/bep/debounce) [![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: ```go func ExampleNew() { var counter uint64 f := func() { atomic.AddUint64(&counter, 1) } debounced, finish, done := debounce.New(100 * time.Millisecond) for i := 0; i < 3; i++ { for j := 0; j < 10; j++ { debounced(f) } time.Sleep(200 * time.Millisecond) } close(finish) <-done c := int(atomic.LoadUint64(&counter)) fmt.Println("Counter is", c) // Output: Counter is 3 } ``` ## Tests To run the tests, you need to install `Leaktest`: ```bash go get -u github.com/fortytw2/leaktest ```debounce-1.1.0/debounce.go000066400000000000000000000032771326234576400154350ustar00rootroot00000000000000// Copyright © 2016 Bjørn Erik Pedersen . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // Package debounce 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. package debounce import ( "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, // 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{}) go func() { for { select { case f := <-out: f() case <-quit: close(out) close(in) close(done) return } } }() debounce := func(f func()) { in <- 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{}) go func() { var f func() = func() {} for { select { case f = <-in: case <-time.After(interval): out <- f <-in // new interval case <-quit: return } } }() return } debounce-1.1.0/debounce_test.go000066400000000000000000000053051326234576400164660ustar00rootroot00000000000000package debounce_test import ( "fmt" "sync" "sync/atomic" "testing" "time" "github.com/bep/debounce" "github.com/fortytw2/leaktest" ) func TestDebounce(t *testing.T) { defer leaktest.Check(t)() var ( counter1 uint64 counter2 uint64 ) f1 := func() { atomic.AddUint64(&counter1, 1) } f2 := func() { atomic.AddUint64(&counter2, 1) } f3 := func() { atomic.AddUint64(&counter2, 2) } debounced, shutdown, done := debounce.New(100 * time.Millisecond) for i := 0; i < 3; i++ { for j := 0; j < 10; j++ { debounced(f1) } time.Sleep(200 * time.Millisecond) } for i := 0; i < 4; i++ { for j := 0; j < 10; j++ { debounced(f2) } for j := 0; j < 10; j++ { debounced(f3) } time.Sleep(200 * time.Millisecond) } close(shutdown) <-done c1 := int(atomic.LoadUint64(&counter1)) c2 := int(atomic.LoadUint64(&counter2)) if c1 != 3 { t.Error("Expected count 3, was", c1) } if c2 != 8 { t.Error("Expected count 8, was", c2) } } func TestDebounceInParallel(t *testing.T) { defer leaktest.Check(t)() var counter uint64 f := func() { atomic.AddUint64(&counter, 1) } debounced, shutdown, done := debounce.New(100 * time.Millisecond) var wg sync.WaitGroup for i := 0; i < 20; 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 }() } wg.Wait() close(shutdown) <-done c := int(atomic.LoadUint64(&counter)) if c != 21 { t.Error("Expected count 21, was", c) } } func TestDebounceCloseEarly(t *testing.T) { defer leaktest.Check(t)() var counter uint64 f := func() { atomic.AddUint64(&counter, 1) } debounced, finish, done := debounce.New(100 * time.Millisecond) debounced(f) close(finish) <-done c := int(atomic.LoadUint64(&counter)) if c != 0 { t.Error("Expected count 0, was", c) } } func BenchmarkDebounce(b *testing.B) { var counter uint64 f := func() { atomic.AddUint64(&counter, 1) } debounced, finish, done := 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 { b.Fatal("Expected count 0, was", c) } } func ExampleNew() { var counter uint64 f := func() { atomic.AddUint64(&counter, 1) } debounced, finish, done := debounce.New(100 * time.Millisecond) for i := 0; i < 3; i++ { for j := 0; j < 10; j++ { debounced(f) } time.Sleep(200 * time.Millisecond) } close(finish) <-done c := int(atomic.LoadUint64(&counter)) fmt.Println("Counter is", c) // Output: Counter is 3 }