10

Underscore provides the method, throttle. From their docs:

Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.

Now imagine the case of an autocomplete form. This means that if 'abc' is typed within a, say, 100ms window, then only a search for 'a' will be sent, and not 'bc'.

Is this a drastic oversight on the part of underscore.js? What would you suggest as a clean solution?

Ross
  • 13,870
  • 11
  • 59
  • 89
Peter Ehrlich
  • 6,794
  • 4
  • 45
  • 61

5 Answers5

5

For this use case, you may want to use the following "buffer" function, which will apply only the last call within the wait window.

https://gist.github.com/2484196

_.buffer = function(func, wait, scope) {
  var timer = null;
  return function() {
    if(timer) clearTimeout(timer);
    var args = arguments;
    timer = setTimeout(function() {
      timer = null;
      func.apply(scope, args);
    }, wait);
  };
};
pedroteixeira
  • 766
  • 6
  • 7
  • 1
    Nice solution, +1. Still, I'd not add it to the `_` object because this may confuse someone that reads your code. *"Was this method from an older version of Underscore? Is this a [Lo-Dash](http://lodash.com/) extension?"* – Camilo Martin Sep 13 '14 at 23:52
4

Ah, I didn't read the docs right! There is a function for this.

var test = _.debounce(function() { console.log('foo'); }, 3000);

Then if you call test() a few times you'll notice that only after three seconds since the last call, will the function ever be called.

This is exactly what we both were looking for... and didn't notice was right below throttle on the docs.

Underscore docs, Lo-Dash docs (what is LoDash?)

Community
  • 1
  • 1
Camilo Martin
  • 35,841
  • 20
  • 107
  • 152
  • 2
    While this seems to work in this use case, debounce and throttle give a different behavior. I'm looking for a way to keep the requests going through while typing, but still ensure the last call (as in the question here). – Joe Hidakatsu Jun 14 '21 at 02:52
1

It looks like the other answers mention using debounce instead of throttle. I found a solution that allows you to keep throttle behavior (keeping function calls going through) while still ensuring the last call.

https://gist.github.com/ayushv512/a2f963bface38f5e2f6f6bba39bba9b9#file-throttle-js

 const throttle = (func, limit) => {
  let lastFunc;
  let lastRan;
  return function() {
    const context = this;
    const args = arguments;
    if (!lastRan) {
      func.apply(context, args)
      lastRan = Date.now();
    } else {
      clearTimeout(lastFunc);
      lastFunc = setTimeout(function() {
          if ((Date.now() - lastRan) >= limit) {
            func.apply(context, args);
            lastRan = Date.now();
          }
       }, limit - (Date.now() - lastRan));
    }
  }
}
Joe Hidakatsu
  • 360
  • 3
  • 16
1

I keep an idle function around to handle this type of user interaction. The idea is to require some function to be called at a regular interval (i.e, when a key is pressed in an input); as soon as the interval passes without a call to said function, a separate callback is triggered. This might not be quite the behavior you want for your autocomplete (you might want to start searching before the user's input has paused), but it's one approach I've used in the past.

If anyone has a better solution for this, though, I'm all ears!

rjz
  • 15,582
  • 3
  • 33
  • 34
  • that's good to know (especially the _ mixin!), but not exactly the same- I definitely want results to appear while people type. – Peter Ehrlich Mar 08 '12 at 05:29
  • Yeah, I hear you. I think the tricky part is deciding where to draw the line with respect to the user's typing speed and the delay in getting a response from the AJAX request. Without some kind of googlesque pre-fetch, it seems like the request will (necessarily) lag behind user input. More pondering needed :-) – rjz Mar 08 '12 at 05:35
0

Yes, you are right that it won't work well for an autocomplete form. I think it's meant for other types of things, though. The example given is handling scroll events.

nicholaides
  • 18,686
  • 12
  • 62
  • 82