@evanmpollack/ps-and-qs
Version:
An efficient promise pool implementation that provides control over the concurrency limit and execution order when running a series of asynchronous tasks.
2 lines (1 loc) • 3.71 kB
JavaScript
var PromisePool=function(){"use strict";class t extends Error{constructor(){super("Operation not allowed on queue of size 0")}}class e{#t;#e;#r;get size(){return this.#r}get empty(){return!this.size}constructor(){this.#t=null,this.#e=null,this.#r=0}static async fromIterable(t){const r=new e;for await(const e of t)r.enqueue(e);return r}enqueue(t){const e={data:t,next:null};this.#e&&(this.#e.next=e),this.#e=e,this.#t=this.#t??this.#e,this.#r++}dequeue(){if(this.empty)throw new t;const{data:e,next:r}=this.#t;return this.#t.next=null,this.#t===this.#e&&(this.#e=null),this.#t=r,this.#r--,e}*[Symbol.iterator](){let t=this.#t;for(;t;)yield t.data,t=t.next}}class r{#s;#i;get size(){return this.#s.length}get empty(){return!this.size}constructor(t){this.#s=[],this.#i=t}static async fromIterable(t,e){const s=[];for await(const e of t)s.push(e);const i=new r(e);return i.#s=r.#o(s,i.#i),i}enqueue(t){this.#s.push(t),r.#a(this.#s,this.#i)}dequeue(){if(this.empty)throw new t;r.#n(this.#s,0,this.#s.length-1);const e=this.#s.pop();return r.#h(this.#s,0,this.#i),e}static#o(t,e){for(let s=Math.floor(t.length/2)-1;s>=0;s--)r.#h(t,s,e);return t}static#a(t,e){let r=t.length-1;const s=t=>Math.floor((t-1)/2);for(;s(r)>=0&&e(t[r],t[s(r)])<0;)this.#n(t,r,s(r)),r=s(r)}static#h(t,e,r){const s=t=>2*t+1,i=t=>2*t+2;for(;s(e)<t.length;){const o=i(e)<t.length&&r(t[i(e)],t[s(e)])<0?i(e):s(e);if(!(r(t[e],t[o])>0))break;this.#n(t,e,o),e=o}}static#n(t,e,r){const s=t[e];t[e]=t[r],t[r]=s}*[Symbol.iterator](){const t=new r(this.#i);for(this.#s.forEach((e=>t.enqueue(e)));!t.empty;)yield t.dequeue()}}class s{#c;#u;#p;get#l(){return!this.#c.empty}constructor(t,e){this.#c=t,this.#u=e,this.#p=[]}async start(){const t=Math.min(this.#u,this.#c.size),e=Array.from({length:t},this.#y.bind(this));return await Promise.all(e),this.#p}async#y(){const t=this.#c.dequeue(),e=this.#m(t),r=await this.#f(e);if(this.#p.push(r),this.#l)return this.#y()}#m(t){let e,r="task";return e=null!==t&&"object"==typeof t&&r in t?"function"!=typeof t.task?()=>Promise.resolve(t.task):t.task:()=>Promise.reject(`Cannot find ${r} property in ${JSON.stringify(t)}`),e}async#f(t){let e;try{e=await Promise.allSettled([t()])}catch(t){e=await Promise.allSettled([Promise.reject(t)])}return e[0]}}class i extends Error{constructor(t){super(t)}}const o=[],a=(t,e)=>e.priority-t.priority;return class{#w;#u;#d;#i;set tasks(t){if(!("function"==typeof t?.[Symbol.iterator]||"function"==typeof t?.[Symbol.asyncIterator]))throw new i("Tasks must be an iterable");this.#w=t}get tasks(){return this.#w}set concurrency(t){if("number"!=typeof t)throw new i("Concurrency must be a Number");if(t<=0)throw new i("Concurrency limit must be greater than 0");this.#u=t}get concurrency(){return this.#u}set priority(t){if("boolean"!=typeof t)throw new i("Priority must be a Boolean");this.#d=t}get priority(){return this.#d}set comparator(t){if("function"!=typeof t)throw new i("Comparator must be a Function");this.#i=t}get comparator(){return this.#i}constructor(t,{concurrency:e,priority:r,comparator:s}={}){this.tasks=t??o,this.concurrency=e??100,this.priority=r??false,this.comparator=s??a}withTasks(t){return this.tasks=t,this}static withTasks(t){return new this(t)}withConcurrency(t){return this.concurrency=t,this}static withConcurrency(t){return new this(void 0,{concurrency:t})}withPriority(){return this.priority=!0,this}static withPriority(){return new this(void 0,{priority:!0})}withComparator(t){return this.comparator=t,this}static withComparator(t){return new this(void 0,{comparator:t})}async start(){const t=await(this.priority?r.fromIterable(this.tasks,this.comparator):e.fromIterable(this.tasks));return await new s(t,this.concurrency).start()}}}();