Debounce a method but keep merging and memoizing the parameters

Viewed 432

I have a method defined like this

updateHook(obj) {
  // update the item
}
// https://www.npmjs.com/package/throttle-debounce
const update = debounce(updateHook, 2000);

I want to merge all the params and call it once if the method is called multiple times within 2 seconds e.g.

update({ name: 'abc' }); // first call
update({ city: 'def' }); // second call
update({ cell: 123 }); // third call
.... ~2 seconds
// should send one call to update with all the params merged like
update({ name: 'abc', city: 'def', cell: 123 });

NOTE: Its not required to use debounce, the requirement is I just want to call a method with merged params if that method is called multiple times during a time period of 2 seconds.

2 Answers

You could write your own higher order function that calls the given function with merged args (see merge in the example below).

The biggest problem here is (IMO) that the merge function makes very strong assumptions about what parameters will be passed.
You'll need to judge for yourself whether you need a highly generic or very specific implementation.

const {debounce} = throttleDebounce;

const merge = (fn) => {
    let merge = {};

    return (obj) => {
        merge = {...merge, ...obj};
        fn(merge);
    };
};

const updateHook = (obj) => {
    console.log(obj);
}

const update = merge(debounce(2000, updateHook));

update({ name: 'abc' });
update({ city: 'def' });
update({ cell: 123 });
<script src="https://unpkg.com/throttle-debounce@3.0.1/umd/index.js"></script>


Another problem would be the probable need to reset the merge function at some point. If this should correlate with the debounce timeout, then a you'll probably need a specialized debounce factory.

const {debounce} = throttleDebounce;

const updateHook = (obj) => {
    console.log(obj);
}

const mergedDebounce = (delay, callback) => {
    let merged = {};

    const debounced = debounce(delay, () => {
        callback(merged);
        merged = {};
    });

    return (obj) => {
        merged = {...merged, ...obj};
        debounced(merged);
    };
};

const update = mergedDebounce(2000, updateHook);

update({ name: 'abc' }); // first call
update({ city: 'def' }); // second call
update({ cell: 123 }); // third call

setTimeout(() => {
    update({ foo: 42 }); // fourth call
}, 2500);
<script src="https://unpkg.com/throttle-debounce@3.0.1/umd/index.js"></script>

function updateHook(obj) {
  // update the item
  console.log('updateHook :: obj ...', obj);
}
const debounce = _.debounce;

function mergeMemoizeAndProceedWithBoundUpdateContext(data) {
  // initialize all data from the bound update context.
  const { memoizedData, proceed } = this;

  Object.assign(memoizedData, data);  // - merge into memoized data.
  proceed(memoizedData);              // - proceed with debounced function.
}
const updateData = mergeMemoizeAndProceedWithBoundUpdateContext.bind({

  memoizedData: {},                   // - provide the memoized data default.
  proceed: debounce(updateHook, 2000) // - create the debounced proceed function.

}); // create update function with bound memoization/update context.

updateData({ name: 'abc' });
updateData({ city: 'def' });
updateData({ cell: 123 });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

The above provided approach remains valid too for solving the currently addressed problem of clearing the memoized data by a correct timing. One just needs to provide and use an additional clearData method at the bound update context ...

const debounce = _.debounce;

function updateHook(obj) {
  // update the item
  console.log('updateHook :: obj ...', obj);
}
const updateThreshold = 2000;

function mergeMemoizeAndProceedWithBoundUpdateContext(data) {
  const { memoizedData, clearData, proceed } = this;

  Object.assign(memoizedData, data);
  proceed(memoizedData);

  clearData.call(this);
}

function clearMemoizedData() {
  this.memoizedData = {};
}
const updateData = mergeMemoizeAndProceedWithBoundUpdateContext.bind({
  memoizedData: {},
  clearData: debounce(clearMemoizedData, updateThreshold),
  proceed: debounce(updateHook, updateThreshold),
});

setTimeout(updateData, 2500, { foo: 42 });
setTimeout(updateData, 6000, { baz: 'baz' });
setTimeout(updateData, 4000, { bar: 'bar' });

updateData({ name: 'abc' });
updateData({ city: 'def' });
updateData({ cell: 123 });

setTimeout(updateData, 9000, { biz: 'biz' });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

Related