-
-
Save swannodette/5888989 to your computer and use it in GitHub Desktop.
(defn debounce | |
([c ms] (debounce (chan) c ms)) | |
([c' c ms] | |
(go | |
(loop [start nil loc (<! c)] | |
(if (nil? start) | |
(do | |
(>! c' loc) | |
(recur (js/Date.) nil)) | |
(let [loc (<! c)] | |
(if (>= (- (js/Date.) start) ms) | |
(recur nil loc) | |
(recur (js/Date.) loc)))))) | |
c')) |
// http://stackoverflow.com/questions/13320015/how-to-write-a-debounce-service-in-angularjs | |
app.factory('debounce', function($timeout, $q) { | |
return function(func, wait, immediate) { | |
var timeout; | |
var deferred = $q.defer(); | |
return function() { | |
var context = this, args = arguments; | |
var later = function() { | |
timeout = null; | |
if(!immediate) { | |
deferred.resolve(func.apply(context, args)); | |
deferred = $q.defer(); | |
} | |
}; | |
var callNow = immediate && !timeout; | |
if ( timeout ) { | |
$timeout.cancel(timeout); | |
} | |
timeout = $timeout(later, wait); | |
if (callNow) { | |
deferred.resolve(func.apply(context,args)); | |
deferred = $q.defer(); | |
} | |
return deferred.promise; | |
}; | |
}; | |
}); |
I don't see how that works either. Clojure says, "the recur expression must match the arity of the recursion point exactly".
Looks like a typo. Line #8 should look exactly like line #12
@overthink @dball @ghoseb, thanks for the review, yes that's a typo.
something to consider: pass c' in as an argument, or provide a different arity that gives you your desired default. In the JCSP docs and in my own tests, you often build sets of channels then want to wire them together, having all constructs take channels as arguments allows you to write stuff up after the channels have already been created.
@halgari thanks for the review excellent points
I found given implementation of debounce incorrect. The point of debounce is that it gets fired exactly once after all frequent calls end. This implementation doesn't put into channel when all calls end.
This demo of debounce is correct: Ben Alman » jQuery throttle / debounce » Examples » Debounce
So I wrote bit different debounce which I think is correct
Simple version:
(defn debounce
([c ms] (debounce (chan) c ms))
([c' c ms]
(go
(loop [timeout nil]
(let [loc (<! c)]
(when timeout
(js/clearTimeout timeout))
(let [t (js/setTimeout #(go (>! c' loc)) ms)]
(recur t)))))
c'))
Version with immediate
param. This should be equivalent to above AngularJS snippet. When immediate
true, first call always gets fired instantly and only after that debouncing starts. It might be desired in some cases.
(defn debounce
([c ms] (debounce (chan) c ms false))
([c ms immediate] (debounce (chan) c ms immediate))
([c' c ms immediate]
(go
(loop [start (js/Date.) timeout nil]
(let [loc (<! c)]
(when timeout
(js/clearTimeout timeout))
(let [diff (- (js/Date.) start)
delay (if (and immediate
(or (>= diff ms)
(not timeout)))
0 ms)
t (js/setTimeout #(go (>! c' loc)) delay)]
(recur (js/Date.) t)))))
c'))
See also https://gist.github.com/scttnlsn/9744501 for a version of debounce that outputs the final input after a flurry of inputs.
Does that first recur
(recur (js/Date.))
have the correct arity for the enclosingloop
? I'm confused by that part.