Is it somehow possible using lodash to debounce an async function so it not only be run after a specified delay but also after the latest fired async function finished?
Here is an example:
import _ from "lodash"
const debouncedFunc = _.debounce(async (input: string) => {
return new Promise<void>(resolve => {
console.log(`start ${input}`)
setTimeout(() => {
console.log(`finish ${input}`)
resolve()
}, 5000)
})
}, 1000)
setTimeout(args => {debouncedFunc("0")}, 0)
setTimeout(args => {debouncedFunc("2000")}, 2000)
setTimeout(args => {debouncedFunc("4000")}, 4000)
And it produces the following output:
start 0
start 2000
start 4000
finish 0
finish 2000
finish 4000
And I want it to produce:
start 0
finish 0
start 4000
finish 4000
Does lodash(or some other lib) has means to get this behaviour?
CodePudding user response:
debounce
doesn't provide what you want, but you can extend behaviour by wrapping in a helper function which will do what you want
import _ from 'lodash';
function main() {
console.clear();
const debouncedFunc = _.debounce(
/*⚠️*/debounceAsync(async (input: string) => {
return new Promise<void>((resolve) => {
console.log(`start ${input}`);
setTimeout(() => {
console.log(`finish ${input}`);
resolve();
}, 5000);
});
}),
1000
);
setTimeout((args) => {
debouncedFunc('0');
}, 0);
setTimeout((args) => {
debouncedFunc('2000');
}, 2000);
setTimeout((args) => {
debouncedFunc('4000');
}, 4000);
// Debounce next calls until result's promise resolve
/*⚠️*/function debounceAsync(fn: any) {
let activePromise: any = null;
let cancel: any = null;
const debouncedFn = (...args: any) => {
cancel?.();
if (activePromise) {
const abortController = new AbortController();
cancel = abortController.abort.bind(abortController);
activePromise.then(() => {
if (abortController.signal.aborted) return;
debouncedFn(...args);
});
return;
}
activePromise = Promise.resolve(fn(...args));
activePromise.finally(() => {
activePromise = null;
});
};
return debouncedFn;
}
}
main();
CodePudding user response:
You do not want the item to run if the process is currently running. So to do that, you need to add a parameter into your call that says it is running and when it is done running you need to flip it back.
const limitCalls = (() => {
let isRunning = false;
return (value) => {
console.log("called", value);
if (isRunning) return;
console.log("start", value);
isRunning = true;
return window.setTimeout(() => {
console.log("finish", value);
isRunning = false;
}, 5000);
};
})();
setTimeout(args => {limitCalls("0")}, 0)
setTimeout(args => {limitCalls("2000")}, 2000)
setTimeout(args => {limitCalls("6000")}, 6000)