magnars / dash.el

A modern list library for Emacs
GNU General Public License v3.0
1.67k stars 137 forks source link

Throttle function #267

Open alphapapa opened 6 years ago

alphapapa commented 6 years ago

I was browsing the CL Serapeum library and came across its throttle function. It seems complementary to dash-functional. I have a function in my Emacs config that I would like to throttle (it plays a notification sound when I get messages in matrix-client; I add it as advice to the built-in notification function), and this works well What do you think?

(defun -throttle (func interval)
  "Throttle FUNC: a closure, lambda, or symbol.

If argument is a symbol then install the throttled function over
the original function.  INTERVAL, a number of seconds or a
duration string as used by `timer-duration', determines how much
time must pass before FUNC will be allowed to run again."
  (cl-typecase func
    (symbol
     (when (get func :throttle-original-function)
       (user-error "%s is already throttled" func))
     (put func :throttle-original-documentation (documentation func))
     (put func 'function-documentation
          (concat (documentation func) " (throttled)"))
     (put func :throttle-original-function (symbol-function func))
     (fset func (throttle--wrap (symbol-function func) interval))
     func)
    (function (throttle--wrap func interval))))

(defun -throttle--wrap (func interval)
  "Return the throttled version of FUNC.
INTERVAL, a number of seconds or a duration string as used by
`timer-duration', determines how much time must pass before FUNC
will be allowed to run again."
  (let ((interval (cl-typecase interval
                    ;; Convert interval to seconds
                    (float interval)
                    (integer interval)
                    (string (timer-duration interval))
                    (t (user-error "Invalid interval: %s" interval))))
        last-run-time)
    (lambda (&rest args)
      (when (or (null last-run-time)
                (>= (float-time (time-subtract (current-time) last-run-time))
                    interval))
        (setq last-run-time (current-time))
        (apply func args)))))

Called like:

(-throttle #'matrix-client-notify-sound "1 second")

It could also be de-throttled with a -throttle-restore function that restores the original definition.

Some of this code is borrowed from Chris Wellons' memoize package.

magnars commented 6 years ago

Hi! This looks like a useful function, but it doesn't quite fit the dash-theme. I'd suggest making a separate open source lib with perhaps throttle and debounce in it. :)

basil-conto commented 6 years ago

Maybe I'm just conflating things, but for some reason I had the impression that something like this was already baked into Emacs (though perhaps as part of some specific package, rather than as a general-purpose function). Maybe I'm thinking of progress reporters, or timers, or url.el, I don't know. And all my searches are coming up dry. So feel free to ignore this comment. :)