11

I really appreciate lodash for its debounce and throttle functionality. I believe I understand the uses cases well and have implemented them dozens of times.

However, depending on requirements, there can be a significant and hard to catch error with _.debounce functions having arguments. That is the following:

Assume you have a debounce function called debounceFn that accepts one argument and has a debounce interval of 1000ms.

  • 100ms: debounceFn(1)
  • 200ms: debounceFn(2)
  • 300ms: debounceFn(2)
  • 400ms: debounceFn(1)
  • 500ms: debounceFn(1)

The child function will eventually call with an argument of 1. This works great for resize events where you only care about the last value, but what if you need separate debounced queues depending on arguments? That is, instead of the process called with an argument of 1, have the process called with argument 1 and with argument 2, but only called once (because they are both debounced).

As an extended and slightly more complex example, consider a combination of arguments below, where the combination produces a unique queue.

actual output:

  • a: lime b: kiwi

desired output (order of first two outputs could be flipped)

  • a: apple b: banana
  • a: apple b: orange
  • a: lime b: kiwi

var process = function(a, b) {
  document.writeln('a: ' + a + ' b: ' + b);
};

var debounceProcess = _.debounce(function(a, b) {
  process(a, b);
}, 1000);


setTimeout(function() {
  debounceProcess('apple', 'orange');
}, 100);
setTimeout(function() {
  debounceProcess('apple', 'banana');
}, 200);
setTimeout(function() {
  debounceProcess('apple', 'orange');
}, 300);
setTimeout(function() {
  debounceProcess('lime', 'kiwi');
}, 400);
<script src="https://cdn.rawgit.com/lodash/lodash/4.6.1/dist/lodash.min.js"></script>

I have seen many SO questions on _.debounce accepting an argument. That is no longer an interesting question, so - how do you create separate debounce queues?

What is an elegant way (simple, easy to read code) to accomplish this using the Lodash _.debounce function, Lodash library and JavaScript native capability? Perhaps a combination of _.debounce and _.memoize (I have tried wrapping _.debounce with _.memoize but will need to explore to understand memoize further). Or perhaps a function to 'hash' the arguments and create a new _.debounce queue for each combination of arguments?

ryanm
  • 2,239
  • 21
  • 31

1 Answers1

6

Store the different debounced functions in an object, where the key represents the arguments. This works well in your use-case, because the arguments are strings.

var process = function(a, b) {
  $('body').append($('<p>').text('a: ' + a + ' b: ' + b));
};

function debounceProcess(a, b) {
  if (! debounceProcess.queues)    { debounceProcess.queues = {}; }
  if (! debounceProcess.queues[a]) { debounceProcess.queues[a] = {}; }
  if (! debounceProcess.queues[a][b]) {
    debounceProcess.queues[a][b] = _.debounce(function(a, b) {
      process(a, b);
    }, 1000);
  }
  return debounceProcess.queues[a][b](a, b);
};

setTimeout(function() {
  debounceProcess('apple', 'orange');
}, 100);
setTimeout(function() {
  debounceProcess('apple', 'banana');
}, 200);
setTimeout(function() {
  debounceProcess('apple', 'orange');
}, 300);
setTimeout(function() {
  debounceProcess('lime', 'kiwi');
}, 400);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.rawgit.com/lodash/lodash/4.6.1/dist/lodash.min.js"></script>
Flimm
  • 136,138
  • 45
  • 251
  • 267
  • Wow! Rockin'. Let me chew on that for a bit. In this case, debounceProcess is a local variable that hasn't been initialized, right? Or is that a way to tie into `this` from the function? – ryanm Apr 22 '16 at 20:31
  • `debounceProcess` is a function. Functions are also objects in Javascript, so you can assign properties to it. Here I've assigned the property `debounceProcess.queues`. I'm not using `this` at all. I've edited my answer to use a function declaration `function debounceProcess() { ... }` instead of a function expression `var debounceProcess = function() { ...}` to make this clearer. `debounceProcess[a][b]` is also a function, which is only initialised once for each pair of `a`,`b`. – Flimm Apr 23 '16 at 09:51
  • 1
    excellent answer! Good to know on setting property assignments, I have never thought to do that. For the sake of other reviewers, jQuery of course is not required but is included for the sake of appending an element (to make it clear for others who review). Thanks for the great answer! :) – ryanm Apr 25 '16 at 17:05
  • Can you please post an es6 equivalent code as well for an express/nodejs app? – Yousuf Khan Oct 09 '20 at 07:44