From 8fdc2f74c9f9bd33ce13ec1992d8a150dd46328f Mon Sep 17 00:00:00 2001 From: Marlon Passos <1marlonpassos@gmail.com> Date: Sun, 11 Dec 2022 20:38:08 -0300 Subject: [PATCH] Feature/debounce with cancel and flush (#118) * test(curry - debounce): handle with cancel * test(curry - debounce): handle with flush method and side effects * feat(curry - debounce): inplements *.cancel and *.flush * test(curry - debounce): show case if cancel past invocations * refactor(curry - debounce): improves typing of timer * docs(curry - debounce): talk about cancel and flush methods Co-authored-by: marlon Felipe Passos --- cdn/radash.esm.js | 17 +++++++++--- cdn/radash.js | 17 +++++++++--- cdn/radash.min.js | 2 +- package.json | 4 +-- src/curry.ts | 43 +++++++++++++++++++++++++----- src/tests/curry.test.ts | 58 +++++++++++++++++++++++++++++++++++++---- 6 files changed, 120 insertions(+), 21 deletions(-) diff --git a/cdn/radash.esm.js b/cdn/radash.esm.js index efbce25c..4f23f8cf 100644 --- a/cdn/radash.esm.js +++ b/cdn/radash.esm.js @@ -492,11 +492,22 @@ const memo = (func, { return memoize({}, func, key, ttl); }; const debounce = ({ delay }, func) => { - let timer = null; + let timer = void 0; + let active = true; const debounced = (...args) => { - clearTimeout(timer); - timer = setTimeout(() => func(...args), delay); + if (active) { + clearTimeout(timer); + timer = setTimeout(() => { + active && func(...args); + }, delay); + } else { + func(...args); + } + }; + debounced.cancel = () => { + active = false; }; + debounced.flush = (...args) => func(...args); return debounced; }; const throttle = ({ interval }, func) => { diff --git a/cdn/radash.js b/cdn/radash.js index 3278d478..3ff2b8ff 100644 --- a/cdn/radash.js +++ b/cdn/radash.js @@ -495,11 +495,22 @@ var radash = (function (exports) { return memoize({}, func, key, ttl); }; const debounce = ({ delay }, func) => { - let timer = null; + let timer = void 0; + let active = true; const debounced = (...args) => { - clearTimeout(timer); - timer = setTimeout(() => func(...args), delay); + if (active) { + clearTimeout(timer); + timer = setTimeout(() => { + active && func(...args); + }, delay); + } else { + func(...args); + } + }; + debounced.cancel = () => { + active = false; }; + debounced.flush = (...args) => func(...args); return debounced; }; const throttle = ({ interval }, func) => { diff --git a/cdn/radash.min.js b/cdn/radash.min.js index 3fdfda70..4995f022 100644 --- a/cdn/radash.min.js +++ b/cdn/radash.min.js @@ -1 +1 @@ -var radash=function(i){"use strict";const $=t=>!!t&&t.constructor===Symbol,N=t=>!!t&&t.constructor===Array,_=t=>!!t&&t.constructor===Object,z=t=>t==null||typeof t!="object"&&typeof t!="function",p=t=>!!(t&&t.constructor&&t.call&&t.apply),D=t=>typeof t=="string"||t instanceof String,F=t=>h(t)&&t%1===0,R=t=>h(t)&&t%1!==0,h=t=>{try{return Number(t)===t}catch{return!1}},P=t=>Object.prototype.toString.call(t)==="[object Date]",Z=t=>{if(t===!0||t===!1||t==null)return!0;if(h(t))return t===0;if(P(t))return isNaN(t.getTime());if(p(t)||$(t))return!1;const e=t.length;if(h(e))return e===0;const n=t.size;return h(n)?n===0:Object.keys(t).length===0},S=(t,e)=>{if(Object.is(t,e))return!0;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const n=Reflect.ownKeys(t),r=Reflect.ownKeys(e);if(n.length!==r.length)return!1;for(let c=0;ct.reduce((n,r)=>{const c=e(r);return n[c]||(n[c]=[]),n[c].push(r),n},{});function q(...t){return!t||!t.length?[]:new Array(Math.max(...t.map(({length:e})=>e))).fill([]).map((e,n)=>t.map(r=>r[n]))}function I(t,e){if(!t||!t.length)return{};const n=p(e)?e:N(e)?(r,c)=>e[c]:(r,c)=>e;return t.reduce((r,c,s)=>({...r,[c]:n(c,s)}),{})}const b=(t,e)=>!t||(t.length??0)===0?null:t.reduce(e),U=(t,e)=>(t||[]).reduce((n,r)=>n+(e?e(r):r),0),J=(t,e=void 0)=>t?.length>0?t[0]:e,K=(t,e=void 0)=>t?.length>0?t[t.length-1]:e,T=(t,e,n=!1)=>{if(!t)return[];const r=(s,u)=>e(s)-e(u),c=(s,u)=>e(u)-e(s);return t.slice().sort(n===!0?c:r)},W=(t,e,n="asc")=>{if(!t)return[];const r=(s,u)=>`${e(s)}`.localeCompare(e(u)),c=(s,u)=>`${e(u)}`.localeCompare(e(s));return t.slice().sort(n==="desc"?c:r)},X=(t,e)=>t.reduce((n,r)=>{const c=e(r);return{...n,[c]:(n[c]??0)+1}},{}),Y=(t,e,n)=>{if(!t)return[];if(!e)return[...t];for(let r=0;rr)=>t.reduce((r,c)=>({...r,[e(c)]:n(c)}),{}),H=(t,e,n)=>t.reduce((r,c)=>n(c)?[...r,e(c)]:r,[]),Q=(t,e)=>{const n=e||(r=>r);return b(t,(r,c)=>n(r)>n(c)?r:c)},V=(t,e)=>{const n=e||(r=>r);return b(t,(r,c)=>n(r){const n=Math.ceil(t.length/e);return new Array(n).fill(null).map((r,c)=>t.slice(c*e,c*e+e))},x=(t,e)=>{const n=t.reduce((r,c)=>{const s=e?e(c):c;return r[s]?r:{...r,[s]:c}},{});return Object.values(n)};function*k(t,e,n=c=>c,r=1){const c=p(n)?n:()=>n,s=e?t:0,u=e??t;for(let o=s;o<=u&&(yield c(o),!(o+r>u));o+=r);}const A=(t,e,n,r)=>Array.from(k(t,e,n,r)),tt=t=>t.reduce((e,n)=>[...e,...n],[]),et=(t,e,n)=>{if(!t||!e)return!1;const r=n??(s=>s),c=e.reduce((s,u)=>({...s,[r(u)]:!0}),{});return t.some(s=>c[r(s)])},j=(t,e)=>t?t.reduce((n,r)=>{const[c,s]=n;return e(r)?[[...c,r],s]:[c,[...s,r]]},[[],[]]):[[],[]],nt=(t,e,n)=>!e&&!t?[]:e?t?n?t.reduce((r,c)=>{const s=e.find(u=>n(c)===n(u));return s?[...r,s]:[...r,c]},[]):t:[]:t,rt=(t,e,n)=>{if(!t&&!e)return[];if(!e)return[...t];if(!t)return[e];for(let r=0;r{if(!t&&!e)return[];if(!t)return[e];if(!e)return[...t];const c=n?(o,f)=>n(o,f)===n(e,f):o=>o===e;return t.find(c)?t.filter((o,f)=>!c(o,f)):(r?.strategy??"append")==="append"?[...t,e]:[e,...t]},it=t=>t?.filter(e=>!!e)??[],B=(t,e,n)=>{let r=n;for(let c=1;c<=t;c++)r=e(r,c);return r},st=(t,e,n=r=>r)=>{if(!t?.length&&!e?.length)return[];if(t?.length===void 0)return[...e];if(!e?.length)return[...t];const r=e.reduce((c,s)=>({...c,[n(s)]:!0}),{});return t.filter(c=>!r[n(c)])};function ut(t,e){if(t.length===0)return t;const n=e%t.length;return n===0?t:[...t.slice(-n,t.length),...t.slice(0,-n)]}const ot=async(t,e,n)=>{const r=n!==void 0;if(!r&&t?.length<1)throw new Error("Cannot reduce empty array with no init value");const c=r?t:t.slice(1);let s=r?n:t[0];for(const u of c)s=await e(s,u);return s},lt=async(t,e)=>{if(!t)return[];let n=[],r=0;for(const c of t){const s=await e(c,r++);n.push(s)}return n},ft=async t=>{const e=[],n=(s,u)=>e.push({fn:s,rethrow:u?.rethrow??!1}),[r,c]=await m(t)(n);for(const{fn:s,rethrow:u}of e){const[o]=await m(s)(r);if(u)throw o}if(r)throw r;return c};class at extends Error{constructor(e=[]){super();const n=e.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${e.length} errors`,this.stack=e.find(r=>r.stack)?.stack??this.stack,this.errors=e}}const dt=async(t,e,n)=>{const r=e.map((a,y)=>({index:y,item:a})),c=async a=>{const y=[];for(;;){const l=r.pop();if(!l)return a(y);const[d,g]=await m(n)(l.item);y.push({error:d,result:g,index:l.index})}},s=A(1,t).map(()=>new Promise(c)),u=await Promise.all(s),[o,f]=j(T(u.flat(),a=>a.index),a=>!!a.error);if(o.length>0)throw new at(o.map(a=>a.error));return f.map(a=>a.result)},gt=async(t,e)=>{const n=t?.times??3,r=t?.delay,c=t?.backoff??null;for(const s of k(1,n)){const[u,o]=await m(e)(f=>{throw{_exited:f}});if(!u)return o;if(u._exited)throw u._exited;if(s===n)throw u;r&&await O(r),c&&await O(c(s))}},O=t=>new Promise(e=>setTimeout(e,t)),m=t=>async(...e)=>{try{return[void 0,await t(...e)]}catch(n){return[n,void 0]}},ht=(...t)=>(...e)=>t.slice(1).reduce((n,r)=>r(n),t[0](...e)),mt=(...t)=>t.reverse().reduce((e,n)=>n(e)),wt=(t,...e)=>(...n)=>t(...e,...n),yt=(t,e)=>n=>t({...e,...n}),pt=t=>new Proxy({},{get:(e,n)=>t(n)}),bt=(t,e,n,r)=>function(...s){const u=n?n(...s):JSON.stringify({args:s}),o=t[u];if(o!==void 0&&o.exp>new Date().getTime())return o.value;const f=e(...s);return t[u]={exp:new Date().getTime()+r,value:f},f},kt=(t,{key:e=null,ttl:n=300}={})=>bt({},t,e,n),At=({delay:t},e)=>{let n=null;return(...c)=>{clearTimeout(n),n=setTimeout(()=>e(...c),t)}},Ot=({interval:t},e)=>{let n=!0;return(...c)=>{!n||(e(...c),n=!1,setTimeout(()=>{n=!0},t))}},Ct=(t,e)=>{const n=()=>{};return new Proxy(Object.assign(n,t),{get:(r,c)=>r[c],set:(r,c,s)=>(r[c]=s,!0),apply:(r,c,s)=>e(Object.assign({},r))(...s)})},Et=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseFloat(t);return isNaN(r)?n:r},$t=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseInt(t);return isNaN(r)?n:r},Nt=(t,e=n=>n===void 0)=>t?Object.keys(t).reduce((r,c)=>e(t[c])?r:{...r,[c]:t[c]},{}):{},C=(t,e)=>Object.keys(t).reduce((r,c)=>({...r,[e(c,t[c])]:t[c]}),{}),_t=(t,e)=>Object.keys(t).reduce((r,c)=>({...r,[c]:e(t[c],c)}),{}),zt=(t,e)=>t?Object.entries(t).reduce((n,[r,c])=>{const[s,u]=e(r,c);return{...n,[s]:u}},{}):{},Pt=t=>t?Object.keys(t).reduce((n,r)=>({...n,[t[r]]:r}),{}):{},St=t=>C(t,e=>e.toLowerCase()),Tt=t=>C(t,e=>e.toUpperCase()),jt=t=>{if(z(t))return t;if(typeof t=="function")return t.bind({});const e=new t.constructor;return Object.getOwnPropertyNames(t).forEach(n=>{e[n]=t[n]}),e},Bt=(t,e)=>{if(!t)return[];const n=Object.entries(t);return n.length===0?[]:n.reduce((r,c)=>[...r,e(c[0],c[1])],[])},Lt=(t,e)=>t?e.reduce((n,r)=>(t.hasOwnProperty(r)&&(n[r]=t[r]),n),{}):{},Dt=(t,e)=>t?!e||e.length===0?t:e.reduce((n,r)=>(delete n[r],n),{...t}):{},Ft=(t,e,n=null)=>{const r=e.split(/[\.\[\]]/g);let c=t;for(const s of r){if(c===null||c===void 0)return n;s.trim()!==""&&(c=c[s])}return c===void 0?n:c},L=(t,e)=>!t&&!e?{}:t?e?Object.entries(t).reduce((n,[r,c])=>({...n,[r]:(()=>_(c)?L(c,e[r]):e[r])()}),{}):t:e,E=(t,e)=>Math.floor(Math.random()*(e-t+1)+t),Rt=t=>{const e=t.length;if(e===0)return null;const n=E(0,e-1);return t[n]},Zt=t=>t.map(e=>({rand:Math.random(),value:e})).sort((e,n)=>e.rand-n.rand).map(e=>e.value),Mt=(t,e="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+e;return B(t,r=>r+n.charAt(E(0,n.length-1)),"")},qt=(t,e=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=t.reduce((l,d,g)=>({indexesByKey:{...l.indexesByKey,[e(d)]:g},itemsByIndex:{...l.itemsByIndex,[g]:d}}),{indexesByKey:{},itemsByIndex:{}}),c=(l,d)=>n[e(l)]n[e(l)]>n[e(d)]?l:d,u=()=>r[0],o=()=>r[t.length-1],f=l=>r[n[e(l)]+1]??u(),a=l=>r[n[e(l)]-1]??o();return{min:c,max:s,first:u,last:o,next:f,previous:a,spin:(l,d)=>{if(d===0)return l;const g=Math.abs(d),Gt=g>t.length?g%t.length:g;return A(0,Gt-1).reduce(d>0?f:a,l)}}},w=t=>{if(!t||t.length===0)return"";const e=t.toLowerCase();return e.substring(0,1).toUpperCase()+e.substring(1,e.length)},It=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Ut=t=>{const e=t?.replace(/([A-Z])+/g,w).split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}_${r.toLowerCase()}`)},Jt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},Kt=t=>{const e=t?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},Wt=t=>t?t.split(/(?=[A-Z])|[\.\-\s_]/).map(e=>e.trim()).filter(e=>!!e).map(e=>w(e.toLowerCase())).join(" "):"",Xt=(t,e,n=/\{\{(.+?)\}\}/g)=>Array.from(t.matchAll(n)).reduce((r,c)=>r.replace(c[0],e[c[1]]),t),Yt=(t,e=" ")=>{if(!t)return"";const n=new RegExp(`^[${e}]+|[${e}]+$`,"g");return t.replace(n,"")};return i.alphabetical=W,i.assign=L,i.boil=b,i.callable=Ct,i.camel=It,i.capitalize=w,i.chain=ht,i.clone=jt,i.cluster=v,i.compose=mt,i.counting=X,i.dash=Jt,i.debounce=At,i.defer=ft,i.diff=st,i.draw=Rt,i.first=J,i.flat=tt,i.fork=j,i.get=Ft,i.group=M,i.intersects=et,i.invert=Pt,i.isArray=N,i.isDate=P,i.isEmpty=Z,i.isEqual=S,i.isFloat=R,i.isFunction=p,i.isInt=F,i.isNumber=h,i.isObject=_,i.isPrimitive=z,i.isString=D,i.isSymbol=$,i.iterate=B,i.last=K,i.list=A,i.listify=Bt,i.lowerize=St,i.map=lt,i.mapEntries=zt,i.mapKeys=C,i.mapValues=_t,i.max=Q,i.memo=kt,i.merge=nt,i.min=V,i.objectify=G,i.omit=Dt,i.parallel=dt,i.partial=wt,i.partob=yt,i.pascal=Kt,i.pick=Lt,i.proxied=pt,i.random=E,i.range=k,i.reduce=ot,i.replace=Y,i.replaceOrAppend=rt,i.retry=gt,i.select=H,i.series=qt,i.shake=Nt,i.shift=ut,i.shuffle=Zt,i.sift=it,i.sleep=O,i.snake=Ut,i.sort=T,i.sum=U,i.template=Xt,i.throttle=Ot,i.title=Wt,i.toFloat=Et,i.toInt=$t,i.toggle=ct,i.trim=Yt,i.try=m,i.tryit=m,i.uid=Mt,i.unique=x,i.upperize=Tt,i.zip=q,i.zipToObject=I,i}({}); +var radash=function(i){"use strict";const $=t=>!!t&&t.constructor===Symbol,N=t=>!!t&&t.constructor===Array,_=t=>!!t&&t.constructor===Object,z=t=>t==null||typeof t!="object"&&typeof t!="function",p=t=>!!(t&&t.constructor&&t.call&&t.apply),D=t=>typeof t=="string"||t instanceof String,F=t=>h(t)&&t%1===0,R=t=>h(t)&&t%1!==0,h=t=>{try{return Number(t)===t}catch{return!1}},P=t=>Object.prototype.toString.call(t)==="[object Date]",Z=t=>{if(t===!0||t===!1||t==null)return!0;if(h(t))return t===0;if(P(t))return isNaN(t.getTime());if(p(t)||$(t))return!1;const e=t.length;if(h(e))return e===0;const n=t.size;return h(n)?n===0:Object.keys(t).length===0},S=(t,e)=>{if(Object.is(t,e))return!0;if(t instanceof Date&&e instanceof Date)return t.getTime()===e.getTime();if(t instanceof RegExp&&e instanceof RegExp)return t.toString()===e.toString();if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;const n=Reflect.ownKeys(t),r=Reflect.ownKeys(e);if(n.length!==r.length)return!1;for(let c=0;ct.reduce((n,r)=>{const c=e(r);return n[c]||(n[c]=[]),n[c].push(r),n},{});function q(...t){return!t||!t.length?[]:new Array(Math.max(...t.map(({length:e})=>e))).fill([]).map((e,n)=>t.map(r=>r[n]))}function I(t,e){if(!t||!t.length)return{};const n=p(e)?e:N(e)?(r,c)=>e[c]:(r,c)=>e;return t.reduce((r,c,s)=>({...r,[c]:n(c,s)}),{})}const b=(t,e)=>!t||(t.length??0)===0?null:t.reduce(e),U=(t,e)=>(t||[]).reduce((n,r)=>n+(e?e(r):r),0),v=(t,e=void 0)=>t?.length>0?t[0]:e,J=(t,e=void 0)=>t?.length>0?t[t.length-1]:e,T=(t,e,n=!1)=>{if(!t)return[];const r=(s,u)=>e(s)-e(u),c=(s,u)=>e(u)-e(s);return t.slice().sort(n===!0?c:r)},K=(t,e,n="asc")=>{if(!t)return[];const r=(s,u)=>`${e(s)}`.localeCompare(e(u)),c=(s,u)=>`${e(u)}`.localeCompare(e(s));return t.slice().sort(n==="desc"?c:r)},W=(t,e)=>t.reduce((n,r)=>{const c=e(r);return{...n,[c]:(n[c]??0)+1}},{}),X=(t,e,n)=>{if(!t)return[];if(!e)return[...t];for(let r=0;rr)=>t.reduce((r,c)=>({...r,[e(c)]:n(c)}),{}),G=(t,e,n)=>t.reduce((r,c)=>n(c)?[...r,e(c)]:r,[]),H=(t,e)=>{const n=e||(r=>r);return b(t,(r,c)=>n(r)>n(c)?r:c)},Q=(t,e)=>{const n=e||(r=>r);return b(t,(r,c)=>n(r){const n=Math.ceil(t.length/e);return new Array(n).fill(null).map((r,c)=>t.slice(c*e,c*e+e))},x=(t,e)=>{const n=t.reduce((r,c)=>{const s=e?e(c):c;return r[s]?r:{...r,[s]:c}},{});return Object.values(n)};function*k(t,e,n=c=>c,r=1){const c=p(n)?n:()=>n,s=e?t:0,u=e??t;for(let o=s;o<=u&&(yield c(o),!(o+r>u));o+=r);}const A=(t,e,n,r)=>Array.from(k(t,e,n,r)),tt=t=>t.reduce((e,n)=>[...e,...n],[]),et=(t,e,n)=>{if(!t||!e)return!1;const r=n??(s=>s),c=e.reduce((s,u)=>({...s,[r(u)]:!0}),{});return t.some(s=>c[r(s)])},j=(t,e)=>t?t.reduce((n,r)=>{const[c,s]=n;return e(r)?[[...c,r],s]:[c,[...s,r]]},[[],[]]):[[],[]],nt=(t,e,n)=>!e&&!t?[]:e?t?n?t.reduce((r,c)=>{const s=e.find(u=>n(c)===n(u));return s?[...r,s]:[...r,c]},[]):t:[]:t,rt=(t,e,n)=>{if(!t&&!e)return[];if(!e)return[...t];if(!t)return[e];for(let r=0;r{if(!t&&!e)return[];if(!t)return[e];if(!e)return[...t];const c=n?(o,f)=>n(o,f)===n(e,f):o=>o===e;return t.find(c)?t.filter((o,f)=>!c(o,f)):(r?.strategy??"append")==="append"?[...t,e]:[e,...t]},it=t=>t?.filter(e=>!!e)??[],B=(t,e,n)=>{let r=n;for(let c=1;c<=t;c++)r=e(r,c);return r},st=(t,e,n=r=>r)=>{if(!t?.length&&!e?.length)return[];if(t?.length===void 0)return[...e];if(!e?.length)return[...t];const r=e.reduce((c,s)=>({...c,[n(s)]:!0}),{});return t.filter(c=>!r[n(c)])};function ut(t,e){if(t.length===0)return t;const n=e%t.length;return n===0?t:[...t.slice(-n,t.length),...t.slice(0,-n)]}const ot=async(t,e,n)=>{const r=n!==void 0;if(!r&&t?.length<1)throw new Error("Cannot reduce empty array with no init value");const c=r?t:t.slice(1);let s=r?n:t[0];for(const u of c)s=await e(s,u);return s},lt=async(t,e)=>{if(!t)return[];let n=[],r=0;for(const c of t){const s=await e(c,r++);n.push(s)}return n},ft=async t=>{const e=[],n=(s,u)=>e.push({fn:s,rethrow:u?.rethrow??!1}),[r,c]=await m(t)(n);for(const{fn:s,rethrow:u}of e){const[o]=await m(s)(r);if(u)throw o}if(r)throw r;return c};class at extends Error{constructor(e=[]){super();const n=e.find(r=>r.name)?.name??"";this.name=`AggregateError(${n}...)`,this.message=`AggregateError with ${e.length} errors`,this.stack=e.find(r=>r.stack)?.stack??this.stack,this.errors=e}}const dt=async(t,e,n)=>{const r=e.map((a,y)=>({index:y,item:a})),c=async a=>{const y=[];for(;;){const l=r.pop();if(!l)return a(y);const[d,g]=await m(n)(l.item);y.push({error:d,result:g,index:l.index})}},s=A(1,t).map(()=>new Promise(c)),u=await Promise.all(s),[o,f]=j(T(u.flat(),a=>a.index),a=>!!a.error);if(o.length>0)throw new at(o.map(a=>a.error));return f.map(a=>a.result)},gt=async(t,e)=>{const n=t?.times??3,r=t?.delay,c=t?.backoff??null;for(const s of k(1,n)){const[u,o]=await m(e)(f=>{throw{_exited:f}});if(!u)return o;if(u._exited)throw u._exited;if(s===n)throw u;r&&await O(r),c&&await O(c(s))}},O=t=>new Promise(e=>setTimeout(e,t)),m=t=>async(...e)=>{try{return[void 0,await t(...e)]}catch(n){return[n,void 0]}},ht=(...t)=>(...e)=>t.slice(1).reduce((n,r)=>r(n),t[0](...e)),mt=(...t)=>t.reverse().reduce((e,n)=>n(e)),wt=(t,...e)=>(...n)=>t(...e,...n),yt=(t,e)=>n=>t({...e,...n}),pt=t=>new Proxy({},{get:(e,n)=>t(n)}),bt=(t,e,n,r)=>function(...s){const u=n?n(...s):JSON.stringify({args:s}),o=t[u];if(o!==void 0&&o.exp>new Date().getTime())return o.value;const f=e(...s);return t[u]={exp:new Date().getTime()+r,value:f},f},kt=(t,{key:e=null,ttl:n=300}={})=>bt({},t,e,n),At=({delay:t},e)=>{let n,r=!0;const c=(...s)=>{r?(clearTimeout(n),n=setTimeout(()=>{r&&e(...s)},t)):e(...s)};return c.cancel=()=>{r=!1},c.flush=(...s)=>e(...s),c},Ot=({interval:t},e)=>{let n=!0;return(...c)=>{!n||(e(...c),n=!1,setTimeout(()=>{n=!0},t))}},Ct=(t,e)=>{const n=()=>{};return new Proxy(Object.assign(n,t),{get:(r,c)=>r[c],set:(r,c,s)=>(r[c]=s,!0),apply:(r,c,s)=>e(Object.assign({},r))(...s)})},Et=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseFloat(t);return isNaN(r)?n:r},$t=(t,e)=>{const n=e===void 0?0:e;if(t==null)return n;const r=parseInt(t);return isNaN(r)?n:r},Nt=(t,e=n=>n===void 0)=>t?Object.keys(t).reduce((r,c)=>e(t[c])?r:{...r,[c]:t[c]},{}):{},C=(t,e)=>Object.keys(t).reduce((r,c)=>({...r,[e(c,t[c])]:t[c]}),{}),_t=(t,e)=>Object.keys(t).reduce((r,c)=>({...r,[c]:e(t[c],c)}),{}),zt=(t,e)=>t?Object.entries(t).reduce((n,[r,c])=>{const[s,u]=e(r,c);return{...n,[s]:u}},{}):{},Pt=t=>t?Object.keys(t).reduce((n,r)=>({...n,[t[r]]:r}),{}):{},St=t=>C(t,e=>e.toLowerCase()),Tt=t=>C(t,e=>e.toUpperCase()),jt=t=>{if(z(t))return t;if(typeof t=="function")return t.bind({});const e=new t.constructor;return Object.getOwnPropertyNames(t).forEach(n=>{e[n]=t[n]}),e},Bt=(t,e)=>{if(!t)return[];const n=Object.entries(t);return n.length===0?[]:n.reduce((r,c)=>[...r,e(c[0],c[1])],[])},Lt=(t,e)=>t?e.reduce((n,r)=>(t.hasOwnProperty(r)&&(n[r]=t[r]),n),{}):{},Dt=(t,e)=>t?!e||e.length===0?t:e.reduce((n,r)=>(delete n[r],n),{...t}):{},Ft=(t,e,n=null)=>{const r=e.split(/[\.\[\]]/g);let c=t;for(const s of r){if(c===null||c===void 0)return n;s.trim()!==""&&(c=c[s])}return c===void 0?n:c},L=(t,e)=>!t&&!e?{}:t?e?Object.entries(t).reduce((n,[r,c])=>({...n,[r]:(()=>_(c)?L(c,e[r]):e[r])()}),{}):t:e,E=(t,e)=>Math.floor(Math.random()*(e-t+1)+t),Rt=t=>{const e=t.length;if(e===0)return null;const n=E(0,e-1);return t[n]},Zt=t=>t.map(e=>({rand:Math.random(),value:e})).sort((e,n)=>e.rand-n.rand).map(e=>e.value),Mt=(t,e="")=>{const n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"+e;return B(t,r=>r+n.charAt(E(0,n.length-1)),"")},qt=(t,e=n=>`${n}`)=>{const{indexesByKey:n,itemsByIndex:r}=t.reduce((l,d,g)=>({indexesByKey:{...l.indexesByKey,[e(d)]:g},itemsByIndex:{...l.itemsByIndex,[g]:d}}),{indexesByKey:{},itemsByIndex:{}}),c=(l,d)=>n[e(l)]n[e(l)]>n[e(d)]?l:d,u=()=>r[0],o=()=>r[t.length-1],f=l=>r[n[e(l)]+1]??u(),a=l=>r[n[e(l)]-1]??o();return{min:c,max:s,first:u,last:o,next:f,previous:a,spin:(l,d)=>{if(d===0)return l;const g=Math.abs(d),Yt=g>t.length?g%t.length:g;return A(0,Yt-1).reduce(d>0?f:a,l)}}},w=t=>{if(!t||t.length===0)return"";const e=t.toLowerCase();return e.substring(0,1).toUpperCase()+e.substring(1,e.length)},It=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}${r.charAt(0).toUpperCase()}${r.slice(1)}`)},Ut=t=>{const e=t?.replace(/([A-Z])+/g,w).split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}_${r.toLowerCase()}`)},vt=t=>{const e=t?.replace(/([A-Z])+/g,w)?.split(/(?=[A-Z])|[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.length===1?e[0]:e.reduce((n,r)=>`${n}-${r.toLowerCase()}`)},Jt=t=>{const e=t?.split(/[\.\-\s_]/).map(n=>n.toLowerCase())??[];return e.length===0?"":e.map(n=>n.charAt(0).toUpperCase()+n.slice(1)).join("")},Kt=t=>t?t.split(/(?=[A-Z])|[\.\-\s_]/).map(e=>e.trim()).filter(e=>!!e).map(e=>w(e.toLowerCase())).join(" "):"",Wt=(t,e,n=/\{\{(.+?)\}\}/g)=>Array.from(t.matchAll(n)).reduce((r,c)=>r.replace(c[0],e[c[1]]),t),Xt=(t,e=" ")=>{if(!t)return"";const n=new RegExp(`^[${e}]+|[${e}]+$`,"g");return t.replace(n,"")};return i.alphabetical=K,i.assign=L,i.boil=b,i.callable=Ct,i.camel=It,i.capitalize=w,i.chain=ht,i.clone=jt,i.cluster=V,i.compose=mt,i.counting=W,i.dash=vt,i.debounce=At,i.defer=ft,i.diff=st,i.draw=Rt,i.first=v,i.flat=tt,i.fork=j,i.get=Ft,i.group=M,i.intersects=et,i.invert=Pt,i.isArray=N,i.isDate=P,i.isEmpty=Z,i.isEqual=S,i.isFloat=R,i.isFunction=p,i.isInt=F,i.isNumber=h,i.isObject=_,i.isPrimitive=z,i.isString=D,i.isSymbol=$,i.iterate=B,i.last=J,i.list=A,i.listify=Bt,i.lowerize=St,i.map=lt,i.mapEntries=zt,i.mapKeys=C,i.mapValues=_t,i.max=H,i.memo=kt,i.merge=nt,i.min=Q,i.objectify=Y,i.omit=Dt,i.parallel=dt,i.partial=wt,i.partob=yt,i.pascal=Jt,i.pick=Lt,i.proxied=pt,i.random=E,i.range=k,i.reduce=ot,i.replace=X,i.replaceOrAppend=rt,i.retry=gt,i.select=G,i.series=qt,i.shake=Nt,i.shift=ut,i.shuffle=Zt,i.sift=it,i.sleep=O,i.snake=Ut,i.sort=T,i.sum=U,i.template=Wt,i.throttle=Ot,i.title=Kt,i.toFloat=Et,i.toInt=$t,i.toggle=ct,i.trim=Xt,i.try=m,i.tryit=m,i.uid=Mt,i.unique=x,i.upperize=Tt,i.zip=q,i.zipToObject=I,i}({}); diff --git a/package.json b/package.json index ec50da2d..1a4c76d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "radash", - "version": "10.0.0", + "version": "10.1.0", "description": "Functional utility library - modern, simple, typed, powerful", "main": "dist/cjs/index.cjs", "module": "dist/esm/index.mjs", @@ -48,4 +48,4 @@ "engines": { "node": ">=14.18.0" } -} \ No newline at end of file +} diff --git a/src/curry.ts b/src/curry.ts index 45ec5fa9..a93fad6d 100644 --- a/src/curry.ts +++ b/src/curry.ts @@ -91,21 +91,50 @@ export const memo = ( return memoize({}, func as any, key, ttl) as any as TFunc } +export type DebounceFunction = { + (...args: TArgs): void + /** + * Cancels the debounced function + */ + cancel(): void + /** + * Runs the debounced function immediately + */ + flush(...args: TArgs): void +} + /** * Given a delay and a function returns a new function * that will only call the source function after delay - * milliseconds have passed without any invocations + * milliseconds have passed without any invocations. + * + * The debounce function comes with a `cancel` method + * to cancel delayed `func` invocations and a `flush` + * method to invoke them immediately */ export const debounce = ( { delay }: { delay: number }, func: (...args: TArgs) => any -): ((...args: TArgs) => void) => { - let timer: any = null - const debounced = (...args: TArgs) => { - clearTimeout(timer) - timer = setTimeout(() => func(...args), delay) +) => { + let timer: NodeJS.Timeout | undefined = undefined + let active = true + + const debounced: DebounceFunction = (...args: TArgs) => { + if (active) { + clearTimeout(timer) + timer = setTimeout(() => { + active && func(...args) + }, delay) + } else { + func(...args) + } } - return debounced as unknown as (...args: TArgs) => void + debounced.cancel = () => { + active = false + } + debounced.flush = (...args: TArgs) => func(...args) + + return debounced } /** diff --git a/src/tests/curry.test.ts b/src/tests/curry.test.ts index d59ac741..1c4c3b57 100644 --- a/src/tests/curry.test.ts +++ b/src/tests/curry.test.ts @@ -1,5 +1,6 @@ import { assert } from 'chai' import * as _ from '..' +import { DebounceFunction } from '../curry' describe('curry module', () => { describe('compose function', () => { @@ -147,15 +148,62 @@ describe('curry module', () => { }) describe('debounce function', () => { - test('only executes once when called rapidly', async () => { - let calls = 0 - const func = _.debounce({ delay: 600 }, () => calls++) + let func: DebounceFunction + const mockFunc = jest.fn() + const runFunc3Times = () => { func() func() func() - assert.equal(calls, 0) + } + + beforeEach(() => { + func = _.debounce({ delay: 600 }, mockFunc) + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + test('only executes once when called rapidly', async () => { + runFunc3Times() + expect(mockFunc).toHaveBeenCalledTimes(0) await _.sleep(610) - assert.equal(calls, 1) + expect(mockFunc).toHaveBeenCalledTimes(1) + }) + + test('does not debounce after cancel is called', () => { + runFunc3Times() + expect(mockFunc).toHaveBeenCalledTimes(0) + func.cancel() + runFunc3Times() + expect(mockFunc).toHaveBeenCalledTimes(3) + runFunc3Times() + expect(mockFunc).toHaveBeenCalledTimes(6) + }) + + test('when we call the flush method it should execute the function immediately', () => { + func.flush() + expect(mockFunc).toHaveBeenCalledTimes(1) + }) + + test('continues to debounce after flush is called', async () => { + runFunc3Times() + expect(mockFunc).toHaveBeenCalledTimes(0) + func.flush() + expect(mockFunc).toHaveBeenCalledTimes(1) + func() + expect(mockFunc).toHaveBeenCalledTimes(1) + await _.sleep(610) + expect(mockFunc).toHaveBeenCalledTimes(2) + func.flush() + expect(mockFunc).toHaveBeenCalledTimes(3) + }) + + test('cancels all pending invocations when cancel is called', async () => { + func() + func.cancel() + await _.sleep(610) + expect(mockFunc).toHaveBeenCalledTimes(0) }) })