I'm trying to debounce a save
function that takes the object to be saved as a parameter for an auto-save that fires on keystroke. The debounce stops the save from happening until the user stops typing, or at least that's the idea. Something like:
var save = _.debounce(function(obj) {
...
}, delay);
Where this falls apart is if I try to save two objects in quick succession. Because the debounce doesn't take the passed in object into account, only the second call to save will fire and only one object will be saved.
save(obj1);
save(obj2);
Will only save obj2
, for example.
I could make obj
an instance of a class that has its own save
method that takes care of debouncing saves to just that object. Or keep a list of partial/curried functions somewhere, but I'm hoping there's a one stop solution out there. Something like:
var save = _.easySolution(function(obj) {
...
}, delay);
What I'm looking for the following string of saves to save each object, but only save each object once.
save(obj1);
save(obj2);
save(obj3);
save(obj2);
save(obj2);
save(obj3);
save(obj2);
save(obj1);
EDIT: Something like this, maybe, just not so convoluted, and something that doesn't mutate the obj
with a __save
function?
function save(obj) {
if(!obj.__save) {
obj.__save = _.debounce(_.partial(function(obj) {
...
}, obj), delay);
}
obj.__save();
}
In JavaScript, a debounce function makes sure that your code is only triggered once per user input. Search box suggestions, text-field auto-saves, and eliminating double-button clicks are all use cases for debounce.
You're going to want to create a debounced version of the function for each argument that get's passed. You can do this fairly easily using debounce(), memoize(), and wrap():
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = _.wrap(
_.memoize(() => _.debounce(save), _.property('id')),
(getMemoizedFunc, obj) => getMemoizedFunc(obj)(obj)
)
saveDebounced({ id: 1, name: 'Jim' });
saveDebounced({ id: 2, name: 'Jane' });
saveDebounced({ id: 1, name: 'James' });
// → saving James
// → saving Jane
You can see that 'Jim'
isn't saved because an object with the same ID is saved right after. The saveDebounced()
function is broken down as follows.
The call to _memoize()
is caching the debounced function based on some resolver function. In this example, we're simply basing it on the id
property. So now we have a way to get the debounced version of save()
for any given argument. This is the most important part, because debounce()
has all kinds of internal state, and so we need a unique instance of this function for any argument that might get passed to save()
.
We're using wrap()
to invoke the cached function (or creating it then caching it if it's the first call), and pass the function our object. The resulting saveDebounced()
function has the exact same signature as save()
. The difference is that saveDebounced()
will debounce calls based on the argument.
Note: if you want to use this in a more generic way, you can use this helper function:
const debounceByParam = (targetFunc, resolver, ...debounceParams) =>
_.wrap(
_.memoize(
() => _.debounce(targetFunc, ...debounceParams),
resolver
),
(getMemoizedFunc, ...params) =>
getMemoizedFunc(...params)(...params)
)
// And use it like so
function save(obj) {
console.log('saving', obj.name);
}
const saveDebounced = debounceByParam(save, _.property('id'), 100)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With