UNPKG

timeout-flow

Version:

Fluent, composable, pauseable JavaScript timers and time control flows — plus RAF utilities for frame-based logic.

3 lines (2 loc) 14.6 kB
// TimeoutFlow - Fluent timer + RAF control library function k(e){if(typeof e=="number"){if(!Number.isFinite(e))throw new TypeError(`Expected a finite number of ms, got: ${e}`);return e}if(typeof e!="string")throw new TypeError(`Expected a duration string or number, got ${typeof e}`);let n=e.toLowerCase().trim().match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)$/);if(!n)throw new Error(`Invalid duration format: "${e}". Expected formats like "500ms", "2s", "1.5m", or "1h".`);let i=Number(n[1]);if(!Number.isFinite(i))throw new TypeError(`Invalid duration value: "${n[1]}"`);let o=n[2];switch(o){case"ms":return i;case"s":return i*1e3;case"m":return i*6e4;case"h":return i*36e5;default:throw new Error(`Unsupported duration unit: "${o}"`)}}var I;typeof performance<"u"&&typeof performance.now=="function"?I=()=>performance.now():I=()=>Date.now();var W=class{#s;#o=null;#e;#n=null;#t=!1;#r=!1;#i=!1;constructor(r){this.#s=k(r),this.#e=this.#s}get isRunning(){return this.#t}get isPaused(){return this.#r}get isFinished(){return this.#i}pause(){if(!this.#t)return;clearTimeout(this.#n),this.#n=null;let r=I()-this.#o;this.#e=Math.max(0,this.#e-r),this.#t=!1,this.#r=!0}resume(r,n=null){if(this.#i||this.#t)return;let i=n??this.#e;if(!Number.isFinite(i)||i<0)return;let o=Math.max(0,i);this.#e=o,this.#o=I(),this.#t=!0,this.#r=!1,this.#n=setTimeout(()=>{this.#n=null,this.#t=!1,this.#r=!1,this.#i=!0,this.#e=0,r?.()},o)}cancel(){this.#n&&clearTimeout(this.#n),this.#n=null,this.#t=!1,this.#r=!1,this.#i=!0,this.#e=0}reset(){this.#n&&clearTimeout(this.#n),this.#n=null,this.#o=null,this.#e=this.#s,this.#t=!1,this.#r=!1,this.#i=!1}};var L;typeof DOMException<"u"?L=(e="Aborted")=>new DOMException(e,"AbortError"):L=(e="Aborted")=>{let r=new Error(e);return r.name="AbortError",r};function C(e,r){return e?e.aborted?(r(),()=>{}):(e.addEventListener("abort",r,{once:!0}),()=>e.removeEventListener("abort",r)):()=>{}}function $(e,r){let n=null;return{add(){n||(n=C(e,r))},remove(){n&&(n(),n=null)},reset(){n&&n(),n=null}}}var U=class extends W{#s=null;#o=null;#e=()=>{};constructor(r,n,{onFinish:i,signal:o}={}){super(r),this.#s=o??null,this.#o=()=>this.cancel(),this.#n(),!this.isFinished&&this.resume(()=>{this.#t(),n?.(),i?.()})}#n(){if(!(!this.#s||!this.#o)){if(this.#s.aborted){super.cancel(),this.#t();return}this.#e=C(this.#s,this.#o)}}#t(){this.#e(),this.#e=()=>{},this.#o=null}cancel(){this.isFinished||(this.#t(),super.cancel())}reset(){this.#e(),this.#e=()=>{},this.#o=()=>this.cancel(),super.reset(),this.#n()}};var Q=e=>e!=null&&typeof e=="object"&&!Array.isArray(e);function q(e,r,n,i=0){let o,l,f;if(typeof e=="function"?(o=e,Q(r)?(l=i,f=r):(l=r,f=Q(n)?n:{})):(l=e,o=r,f=Q(n)?n:{}),typeof o!="function")throw new TypeError(`Expected a function as one argument, got: ${typeof o}`);let a=l??i;if(a==null)throw new TypeError("Expected a duration (string/number) for delay");let s=k(a);return{fn:o,delay:s,options:f}}function V(e,r,n){let{fn:i,delay:o,options:l}=q(e,r,n,void 0);return new U(o,i,l)}var z=class{#s;#o;#e;#n=0;#t=!1;#r=null;#i=null;#l=()=>{};constructor(r,n,{max:i=1/0,runImmediately:o=!1,signal:l}={}){if(this.#s=k(r),this.#o=n,this.#e=i,this.#i=l??null,!(this.#e>0)&&this.#e!==1/0){this.#t=!0,this.#l();return}if(this.#i?.aborted){this.#t=!0,this.#l();return}this.#l=C(this.#i,()=>this.cancel()),o?this.#f():this.#u()}get count(){return this.#n}get isRunning(){return!this.#t&&!!this.#r&&this.#r.isRunning}get isPaused(){return!this.#t&&!!this.#r&&this.#r.isPaused}get isFinished(){return this.#t}#a=()=>{this.#r?.cancel(),this.#r=null,this.#t=!0,this.#l(),this.#l=()=>{},this.#i=null};#f=()=>{if(!this.#t){if(this.#i?.aborted){this.#a();return}if(this.#n>=this.#e){this.#a();return}if(this.#o?.(this.#n++),this.#n>=this.#e){this.#a();return}this.#u()}};#u(){if(!this.#t){if(this.#i?.aborted){this.#a();return}this.#r=new W(this.#s),this.#r.resume(this.#f)}}pause(){this.#t||this.#r?.pause()}resume(){if(!this.#t){if(this.#i?.aborted){this.#a();return}if(this.#n>=this.#e){this.#a();return}if(this.#r){this.#r.isPaused?this.#r.resume(this.#f):this.#r.isRunning||this.#u();return}this.#u()}}cancel(){this.#t||this.#a()}reset(r=!1){if(this.#r?.cancel(),this.#r=null,this.#n=0,this.#t=!1,this.#i?.aborted){this.#t=!0,this.#l(),this.#l=()=>{},this.#i=null;return}if(r){if(!(this.#e>0)&&this.#e!==1/0){this.#t=!0,this.#l(),this.#l=()=>{},this.#i=null;return}this.#u()}}};function X(e,r,n){let{fn:i,delay:o,options:l}=q(e,r,n,void 0);return new z(o,i,l)}function at({signal:e}={}){let r=[],n=new Map,i=0,o=!1,l=!1,f=!1,a=!1,s=!1,c=1/0,p=0,b=null,h=null,y=null,m=[],u=!1,x=null,w=null,d,A=$(e,()=>d.cancel()),v=t=>{t&&t.controller&&(t.controller=null)},F=()=>{let t=r[i];t?.controller?.cancel?.(),t&&(t.controller=null)},T=()=>{a||f||(o=!1,l=!1,a=!0,F(),A.remove(),b?.())},R=()=>{f||(o=!1,l=!1,a=!1,f=!0,F(),A.remove())},E=(t,g)=>{if(R(),h){try{h(t,{step:g,index:i})}catch{}return}throw t},D=(t,g)=>{try{return t()}catch(M){E(M,g);return}},P=t=>{v(t),i+=1,queueMicrotask(_)};function _(){if(f||l||a)return;if(e?.aborted){R();return}if(y){let g=n.get(y);if(typeof g=="number")i=g;else{D(()=>{throw new Error(`Label "${y}" not found`)},r[i]);return}y=null}if(m.length>0){let g=m.shift();D(g,r[i])||(u=!0)}if(u){let g=r[i];if(!g||g.type==="label")u=!1;else return P(g)}let t=r[i];if(!t||i>=r.length){s&&++p<c?(i=0,_()):T();return}if(t.type==="label")return P(t);if(t.type==="after"){t.controller=V(()=>D(t.fn,t),t.duration,{signal:e,onFinish:()=>P(t)});return}if(t.type==="every"){if(!(t.times>0)&&t.times!==1/0)return P(t);let g=0,M=X(()=>{if(!(l||f||a)){if(e?.aborted){R();return}if(t.whileCondition&&!D(t.whileCondition,t)||(D(()=>t.fn(g++),t),t.doWhileCondition&&!D(t.doWhileCondition,t))||g>=t.times)return M.cancel(),P(t)}},t.duration,{max:t.times,signal:e});t.controller=M}}return d={after(t,g){return r.push({type:"after",duration:t,fn:g,controller:null}),d},every(t,g,M=1/0){let N=typeof M=="number"?M:M?.max??1/0,S=N===1/0?1/0:Math.max(0,N|0);return r.push({type:"every",duration:t,fn:g,times:S,whileCondition:x,doWhileCondition:w,controller:null}),x=null,w=null,d},loop(t=!0){return s=!0,c=t===!0?1/0:t,d},onFinish(t){return b=t,d},onError(t){return h=t,d},start(){return F(),f=!1,a=!1,l=!1,o=!0,i=0,p=0,A.reset(),e?.aborted||A.add(),e?.aborted?(R(),d):(_(),d)},pause(){l||f||a||(l=!0,r[i]?.controller?.pause?.())},resume(){if(!(!l||f||a)){if(e?.aborted){R();return}l=!1,r[i]?.controller?.resume?.()}},cancel(){R()},reset(){return R(),r.length=0,n.clear(),i=0,p=0,b=null,h=null,y=null,m=[],u=!1,x=null,w=null,o=!1,l=!1,f=!1,a=!1,d},label(t){return n.set(t,r.length),r.push({type:"label",name:t,controller:null}),d},jumpTo(t){return y=t,d},if(t){return m.push(()=>typeof t=="function"?!!t():!!t),d},unless(t){return m.push(()=>typeof t=="function"?!t():!t),d},while(t){return x=()=>typeof t=="function"?!!t():!!t,d},doWhile(t){return w=()=>typeof t=="function"?!!t():!!t,d},get isRunning(){return o&&!l&&!f&&!a},get isPaused(){return l},get isCancelled(){return f},get isFinished(){return a}},d}function ut(e,r,n){let{fn:i,delay:o,options:l}=q(e,r,n,void 0),f=l?.signal,a=l?.trailing??!0,s=Math.max(0,o),c=-1/0,p=0,b=null,h=null,y=$(f,()=>u()),m=()=>{if(!b)return;let d=b,A=h;b=null,h=null,y.remove(),i.apply(A,d)},u=()=>{p&&clearTimeout(p),p=0,b=null,h=null,y.remove()},x=d=>{y.add(),p=setTimeout(()=>{p=0,c=I(),m()},d)},w=function(...d){if(f?.aborted)return;let A=I(),v=A-c;if(b=d,h=this,v>=s){c=A,p&&(clearTimeout(p),p=0,y.remove()),m();return}if(a&&!p){let F=Math.max(0,s-v);x(F)}};return w.cancel=u,w.flush=()=>{p&&(clearTimeout(p),p=0,c=I(),m())},f?.aborted&&u(),w}function ft(e,r,n){let{fn:i,delay:o,options:l}=q(e,r,n,0),f=l?.signal,a=Math.max(0,o),s=0,c=null,p=null,b=$(f,()=>y()),h=()=>{if(!c)return;let u=c,x=p;c=null,p=null,b.remove(),i.apply(x,u)},y=()=>{s&&clearTimeout(s),s=0,c=null,p=null,b.remove()},m=function(...u){f?.aborted||(c=u,p=this,s&&clearTimeout(s),b.add(),s=setTimeout(()=>{s=0,h()},a))};return m.cancel=y,m.flush=()=>{s&&(clearTimeout(s),s=0,h())},f?.aborted&&y(),m}function ct(e,{interval:r="250ms",timeout:n,immediate:i=!1,signal:o}={}){return new Promise((l,f)=>{if(o?.aborted){f(L());return}let a=I(),s=typeof r=="string"?k(r):r,c=n!=null?k(n):null,p=null,b=()=>{p!=null&&(clearInterval(p),p=null),y()},y=C(o,()=>{b(),f(L())}),m=()=>{let u=!1;try{u=!!e()}catch(x){b(),f(x);return}if(u){b(),l();return}c!=null&&I()-a>=c&&(b(),f(new Error("waitFor timed out")))};i&&m(),p=setInterval(m,s)})}async function ht(e,{attempts:r=3,delay:n="500ms",backoff:i=!1,factor:o=2,maxDelay:l,jitter:f=!1,signal:a,random:s=Math.random,shouldRetry:c,onRetry:p}={}){if(typeof e!="function")throw new TypeError("retry: fn must be a function");let b=Math.max(1,r|0);if(a?.aborted)throw L();let h=k(n),y=l!=null?k(l):1/0,m=Math.min(h,y);for(let u=0;u<b;u++){if(a?.aborted)throw L();try{return await e(u)}catch(x){if(u===b-1||c&&!await c(x,u))throw x;let d=i?h*Math.pow(o,u):h,A=mt({expDelay:d,baseDelay:h,maxDelayMs:y,jitter:f,random:s,prevSleep:m});m=A,p&&await p(x,u,A),await pt(A,a)}}throw new Error("retry: exhausted attempts unexpectedly")}function mt({expDelay:e,baseDelay:r,maxDelayMs:n,jitter:i,random:o,prevSleep:l}){let f=Math.min(e,n);if(!i)return f;let a=i===!0?"full":i;if(a==="full")return Math.min(n,Y(0,f,o));if(a==="equal"){let s=f/2;return Math.min(n,s+Y(0,s,o))}if(a==="decorrelated"){let s=Y(r,l*3,o);return Math.min(n,s)}return f}function Y(e,r,n){let i=n(),o=Number.isFinite(i)?Math.min(1,Math.max(0,i)):0;return e+(r-e)*o}function pt(e,r){return new Promise((n,i)=>{if(r?.aborted){i(L());return}let o=0,l=()=>{o&&clearTimeout(o),o=0,a()},a=C(r,()=>{l(),i(L())});o=setTimeout(()=>{l(),n()},e)})}function j(e,r,n,i){let o=typeof n=="function"?n:void 0,l=o?i:n,{fn:f,delay:a,options:s}=q(e,r,l,void 0),{signal:c,onFinish:p}=o?{...s,onFinish:o}:s,b=Math.max(0,a),h=0,y=b,m=null,u=!1,x=!1,w=!1,d=()=>{h&&cancelAnimationFrame(h),h=0},A=()=>{},v=()=>{A(),A=()=>{}},F=()=>{d(),u=!1,x=!1,w=!0,m=null,y=0,v()},T=()=>{F(),f?.(),p?.()},R=()=>{w||F()},E=()=>{if(v(),c?.aborted){w=!0,y=0;return}A=C(c,R)};E();let D=g=>{if(!u||w)return;if(m==null&&(m=g),g-m>=y){T();return}h=requestAnimationFrame(D)},P=()=>{if(!u||w)return;d();let g=I();if(m!=null){let M=g-m;y=Math.max(0,y-M)}m=null,u=!1,x=!0},_=()=>{if(!(w||u||!x)&&!c?.aborted){if(!(y>0)){T();return}x=!1,u=!0,m=null,h=requestAnimationFrame(D)}},t=()=>{d(),m=null,y=b,u=!1,x=!1,w=!1,E()};return w?v():y>0?(u=!0,h=requestAnimationFrame(D)):T(),{pause:P,resume:_,cancel:R,reset:t,get isRunning(){return u},get isPaused(){return x},get isFinished(){return w}}}var G=e=>e!=null&&typeof e=="object"&&!Array.isArray(e);function tt(e,r,n,i,o){let l=typeof e!="function",f,a,s;l?(G(n)?s=n:G(i)?s=i:G(o)?s=o:s={},typeof n=="number"&&(f=n),typeof i=="boolean"&&(a=i),a==null&&typeof n=="boolean"&&(a=n)):s=G(n)?n:{};let{fn:c,delay:p,options:b}=q(e,r,s,void 0),h=b?.signal,y=b?.max??1/0,m=b?.runImmediately??!1,u=f??y,x=a??m,w=Math.max(0,p),d=0,A=0,v=w,F=null,T=!1,R=!1,E=!1,D=()=>{d&&cancelAnimationFrame(d),d=0},P=()=>{},_=()=>{P(),P=()=>{}},t=()=>{D(),T=!1,R=!1,E=!0,F=null,v=0,_()},g=()=>{E||t()},M=()=>{if(_(),h?.aborted){E=!0,v=0;return}P=C(h,g)};M();let N=()=>A<u,S=()=>(c?.(A++),N()?!0:(t(),!1)),H=O=>{if(!T||E)return;if(h?.aborted){t();return}if(!N()){t();return}F==null&&(F=O);let K=O-F;F=O,v-=K;let Z=Math.max(0,w);if(Z===0){v=0,S(),d=requestAnimationFrame(H);return}for(;v<=0&&!E;){if(!S())return;v+=Z}d=requestAnimationFrame(H)},J=()=>{if(!E){if(h?.aborted){t();return}if(!N()){t();return}T=!0,R=!1,F=null,d=requestAnimationFrame(H)}},ot=()=>{if(!T||E)return;D();let O=I();if(F!=null){let K=O-F;v=Math.max(0,v-K)}F=null,T=!1,R=!0},st=()=>{if(!(E||T||!R)&&!h?.aborted){if(!N()){t();return}J()}},lt=(O=!1)=>{if(D(),A=0,v=w,F=null,T=!1,R=!1,E=!1,M(),!E){if(!(u>0)&&u!==1/0){t();return}if(O){if(x&&N()&&(S(),E))return;J()}}};return E?_():!(u>0)&&u!==1/0?t():(x&&N()&&S(),E||(v=Math.max(0,w),J())),{pause:ot,resume:st,cancel:g,reset:lt,get isRunning(){return T},get isPaused(){return R},get isFinished(){return E},get count(){return A}}}function et(e,r,n){let{fn:i,delay:o,options:l}=q(e,r,n,0),f=Math.max(0,o),a=l?.signal,s=0,c=null,p=null,b=0,h=()=>{s&&cancelAnimationFrame(s),s=0},y=$(a,()=>m()),m=()=>{h(),c=null,p=null,b=0,y.remove()},u=()=>{if(!c)return;let d=c,A=p;c=null,p=null,y.remove(),i.apply(A,d)},x=d=>{if(!s)return;if(!c){m();return}if(d-b>=f){h(),u();return}s=requestAnimationFrame(x)},w=function(...d){a?.aborted||(c=d,p=this,b=I(),y.add(),h(),s=requestAnimationFrame(x))};return w.cancel=m,w.flush=()=>{!s||!c||(h(),u())},a?.aborted&&m(),w}var rt=e=>e!=null&&typeof e=="object"&&!Array.isArray(e);function nt(e,r=0,n){if(typeof e!="function")throw new TypeError(`Expected a function, got: ${typeof e}`);let i=typeof r=="number"?r:0,o=rt(r)?r:rt(n)?n:{},l=o.signal,f=o.trailing??!0,a=Math.max(0,i|0),s=!1,c=0,p=null,b=null,h=a,y=$(l,()=>u()),m=()=>{s=!1,c&&cancelAnimationFrame(c),c=0,h=a,p=null,b=null,y.remove()},u=()=>{m()},x=()=>{if(!p)return;let T=p,R=b;p=null,b=null,y.remove(),e.apply(R,T)},w=()=>{s=!1,c=0,h=a,x()},d=()=>{if(s){if(l?.aborted){u();return}if(h===0){w();return}h-=1,c=requestAnimationFrame(d)}},A=()=>{if(s){if(l?.aborted){u();return}w()}},v=a===0?()=>{s=!0,y.add(),c=requestAnimationFrame(A)}:()=>{s=!0,h=a,y.add(),c=requestAnimationFrame(d)},F=function(...T){l?.aborted||((!s||f)&&(p=T,b=this),!s&&v())};return F.cancel=u,F.flush=()=>{if(s){if(l?.aborted){u();return}s=!1,c&&cancelAnimationFrame(c),c=0,h=a,x()}},l?.aborted&&u(),F}function it(e,{timeout:r,signal:n,immediate:i=!1}={}){return new Promise((o,l)=>{if(typeof e!="function"){l(new TypeError(`waitForRaf: condition must be a function, got: ${typeof e}`));return}if(n?.aborted){l(L());return}let f=r!=null?Math.max(0,k(r)):null,a=0,s=null,c=()=>{a&&cancelAnimationFrame(a),a=0,b()},b=C(n,()=>{c(),l(L())}),h=()=>{try{return!!e()}catch(m){return c(),l(m),null}};if(i){let m=h();if(m===null)return;if(m){c(),o();return}if(f===0){c(),l(new Error("waitForRaf timed out"));return}}let y=m=>{s==null&&(s=m);let u=h();if(u!==null){if(u){c(),o();return}if(f!=null&&m-s>=f){c(),l(new Error("waitForRaf timed out"));return}a=requestAnimationFrame(y)}};a=requestAnimationFrame(y)})}function dt(){if(typeof requestAnimationFrame!="function")throw new Error("requestAnimationFrame is not available in this environment. Use the non-RAF timers (after/every/waitFor) in Node, or provide a RAF polyfill.")}var B=e=>(...r)=>(dt(),e(...r)),yt=B(j),bt=B(tt),wt=B(et),xt=B(nt),At=B(it);export{V as after,yt as afterRaf,ft as debounce,wt as debounceRaf,X as every,bt as everyRaf,at as flow,k as parseDuration,ht as retry,ut as throttle,xt as throttleRaf,ct as waitFor,At as waitForRaf};