Scene
I believe many people have encountered similar scenarios:
A button is used to send a request and takes some time to process it. However, users often click on it intentionally or unintentionally multiple times during processing, so we hope that new requests will not be issued before the last request is processed.
1. Preliminary solution: special matters
The meaning of "special matters" is that every time you encounter such a scene, you will write a paragraph of logic to deal with it:
('click', (() => { let lock = false; return () => { if(lock) return; lock = true; ('clicked'); // Use delay for easy testing setTimeout(() => { lock = false; }, () * 4e3) } })());
2. Conditional callback function based on convention callback
The above writing method is actually not a problem, but can this kind of conditional limit be like a bad interview question?Throttle"and"Anti-shake"In that way, can you achieve the effect by wrapping it with a function?
The key to the problem is actually: the execution condition that needs to be considered for anti-shake and throttling is time. This condition is a "common language" for all functions, so both parties can achieve such a "tactic understanding".
To achieve the same effect in this scenario, both parties need to deliberately agree: for example, the function executed by the condition accepts an additional function to be used to lift the conditional limit at the right time.
function conditioned(callback:(release:Function,...args:any[]) => any){ let lock = false; return function(...args:any[]){ if(lock) return; lock = true; (this, () => { lock = false; }, ...args); } }
👆 To facilitate the description of this agreementcallback
Requirements, TS is used instead of JS.
When using:
('click', conditioned((release) => { return () => { ('clicked'); setTimeout(() => { release(); // Release lock }, () * 4e3); } }));
3. Promise-based conditional callback function
If there is any method that can be more "elegant" and "general" than callback functions, the answer is obviouslyPromise
。
Because the above writing method rewritten the parameters of the original callback function, if you encounter a beginner who likes Ctrl + C, he will wonder why the copied function does not work.
function conditioned(callback:(...args:any[]) => Promise<any>){ let lock = false; return function(...args:any[]){ if(lock) return; lock = true; try { await (this, ...args); lock = false; } catch(err) { lock = false; throw err; } } }
How to use:
('click', conditioned(() => { return new Promise((resolve) => { ('clicked'); setTimeout(() => { resolve(); // Release lock }, () * 4e3); }); }));
Although at first glance, using this function means that the return value of the callback function must be rewritten asPromise
, but since this kind of scenarios are often asynchronous operations, it is changed toasync
Why not do it?
4. React hook version
import { useRef } from 'react'; function useCondition(callback: (...args: any[]) => Promise<any>) { const lock = useRef(false); return async (...args:any[]) => { if() return; = true; try{ await callback(...args); = false } catch(error){ = false; throw error; } }; }
How to use:
<button onClick={useCondition(() => { return new Promise<void>((resolve) => { ('clicked'); setTimeout(() => { resolve(); // Release lock }, () * 4e3); }); })} >test</button>
👆I haven't actually tested it, I don't know if it works. For more information about JS's ban on new operations last time, please pay attention to my other related articles!