promise-parallel-throttle
Version:
Run promises in parallel, but throttled
1 lines • 9.78 kB
Source Map (JSON)
{"version":3,"file":"throttle.mjs","sources":["../../../src/throttle.ts"],"sourcesContent":["const DEFAULT_MAX = 5;\n\nexport interface Result<T> {\n lastCompletedIndex: number;\n amountDone: number;\n amountStarted: number;\n amountResolved: number;\n amountRejected: number;\n amountNextCheckFalsey: number;\n rejectedIndexes: number[];\n resolvedIndexes: number[];\n nextCheckFalseyIndexes: number[];\n taskResults: T[];\n}\n\nexport interface Options<T> {\n maxInProgress?: number;\n failFast?: boolean;\n progressCallback?: (result: Result<T>) => void;\n nextCheck?: nextTaskCheck<T>;\n ignoreIsFunctionCheck?: boolean;\n}\n\n/**\n * Default checker which validates if a next task should begin.\n * This can be overwritten to write own checks for example checking the amount\n * of used ram and waiting till the ram is low enough for a next task.\n *\n * It should always resolve with a boolean, either `true` to start a next task\n * or `false` to stop executing a new task.\n *\n * If this method rejects, the error will propagate to the caller\n * @param status\n * @param tasks\n * @returns {Promise}\n */\nconst defaultNextTaskCheck: nextTaskCheck<unknown> = <T>(status: Result<T>, tasks: Tasks<T>): Promise<boolean> => {\n return Promise.resolve(status.amountStarted < tasks.length);\n};\n\nconst DEFAULT_OPTIONS = {\n maxInProgress: DEFAULT_MAX,\n failFast: false,\n nextCheck: defaultNextTaskCheck,\n ignoreIsFunctionCheck: false,\n};\n\nexport type Task<T> = () => Promise<T>;\nexport type Tasks<T> = Array<Task<T>>;\nexport type nextTaskCheck<T> = (status: Result<T>, tasks: Tasks<T>) => Promise<boolean>;\n\n/**\n * Raw throttle function, which can return extra meta data.\n * @param tasks required array of tasks to be executed\n * @param options Options object\n * @returns {Promise}\n */\nexport function raw<T>(tasks: Tasks<T>, options?: Options<T>): Promise<Result<T>> {\n return new Promise<Result<T>>((resolve, reject) => {\n const myOptions = Object.assign({}, DEFAULT_OPTIONS, options);\n const result: Result<T> = {\n lastCompletedIndex: -1,\n amountDone: 0,\n amountStarted: 0,\n amountResolved: 0,\n amountRejected: 0,\n amountNextCheckFalsey: 0,\n rejectedIndexes: [],\n resolvedIndexes: [],\n nextCheckFalseyIndexes: [],\n taskResults: [],\n };\n\n if (tasks.length === 0) {\n return resolve(result);\n }\n\n let failedFast = false;\n let currentTaskIndex = 0;\n\n const handleError = (error: T, index: number) => {\n result.taskResults[index] = error;\n result.rejectedIndexes.push(index);\n result.amountRejected++;\n if (myOptions.failFast === true) {\n result.lastCompletedIndex = index;\n failedFast = true;\n return reject(result);\n }\n taskDone(index);\n };\n\n const executeTask = (index: number) => {\n result.amountStarted++;\n if (typeof tasks[index] === 'function') {\n try {\n tasks[index]().then(\n (taskResult) => {\n result.taskResults[index] = taskResult;\n result.resolvedIndexes.push(index);\n result.amountResolved++;\n taskDone(index);\n },\n (error) => {\n handleError(error, index);\n },\n );\n } catch (err) {\n handleError(err as T, index);\n }\n } else if (myOptions.ignoreIsFunctionCheck === true) {\n result.taskResults[index] = tasks[index] as any as T;\n result.resolvedIndexes.push(index);\n result.amountResolved++;\n taskDone(index);\n } else {\n failedFast = true;\n return reject(\n new Error('tasks[' + index + ']: ' + tasks[index] + ', is supposed to be of type function'),\n );\n }\n };\n\n const taskDone = (index: number) => {\n //make sure no more tasks are spawned when we failedFast\n if (failedFast === true) {\n return;\n }\n\n result.amountDone++;\n result.lastCompletedIndex = index;\n if (typeof myOptions.progressCallback === 'function') {\n myOptions.progressCallback(result);\n }\n if (result.amountDone === tasks.length) {\n return resolve(result);\n }\n if (currentTaskIndex < tasks.length) {\n nextTask(currentTaskIndex++);\n }\n };\n\n const nextTask = (index: number) => {\n //check if we can execute the next task\n myOptions.nextCheck(result, tasks).then((canExecuteNextTask) => {\n if (canExecuteNextTask === true) {\n //execute it\n executeTask(index);\n } else {\n result.amountNextCheckFalsey++;\n result.nextCheckFalseyIndexes.push(index);\n taskDone(index);\n }\n }, reject);\n };\n\n //spawn the first X task\n for (let i = 0; i < Math.min(myOptions.maxInProgress, tasks.length); i++) {\n nextTask(currentTaskIndex++);\n }\n });\n}\n\n/**\n * Executes the raw function, but only return the task array\n * @param tasks\n * @param options\n * @returns {Promise}\n */\nfunction executeRaw<T>(tasks: Tasks<T>, options: Options<T>): Promise<T[]> {\n return new Promise<T[]>((resolve, reject) => {\n raw(tasks, options).then(\n (result: Result<T>) => {\n resolve(result.taskResults);\n },\n (error: Error | Result<T>) => {\n if (error instanceof Error) {\n reject(error);\n } else {\n reject(error.taskResults[error.rejectedIndexes[0]]);\n }\n },\n );\n });\n}\n\n/**\n * Simply run all the promises after each other, so in synchronous manner\n * @param tasks required array of tasks to be executed\n * @param options Options object\n * @returns {Promise}\n */\nexport function sync<T>(tasks: Tasks<T>, options?: Options<T>): Promise<T[]> {\n const myOptions = Object.assign({}, {maxInProgress: 1, failFast: true}, options);\n return executeRaw(tasks, myOptions);\n}\n\n/**\n * Exposes the same behaviour as Promise.All(), but throttled!\n * @param tasks required array of tasks to be executed\n * @param options Options object\n * @returns {Promise}\n */\nexport function all<T>(tasks: Tasks<T>, options?: Options<T>): Promise<T[]> {\n const myOptions = Object.assign({}, {failFast: true}, options);\n return executeRaw(tasks, myOptions);\n}\n"],"names":["DEFAULT_OPTIONS","maxInProgress","failFast","nextCheck","status","tasks","Promise","resolve","amountStarted","length","ignoreIsFunctionCheck","raw","options","reject","myOptions","Object","assign","result","lastCompletedIndex","amountDone","amountResolved","amountRejected","amountNextCheckFalsey","rejectedIndexes","resolvedIndexes","nextCheckFalseyIndexes","taskResults","failedFast","currentTaskIndex","handleError","error","index","push","taskDone","progressCallback","nextTask","then","canExecuteNextTask","taskResult","err","Error","executeTask","i","Math","min","executeRaw","sync","all"],"mappings":"AAAA,IAwCMA,EAAkB,CACpBC,cAzCgB,EA0ChBC,UAAU,EACVC,UAPiD,SAAIC,EAAmBC,GACxE,OAAOC,QAAQC,QAAQH,EAAOI,cAAgBH,EAAMI,OACxD,EAMIC,uBAAuB,GAaX,SAAAC,EAAON,EAAiBO,GACpC,OAAO,IAAIN,SAAmB,SAACC,EAASM,GACpC,IAAMC,EAAYC,OAAOC,OAAO,CAAA,EAAIhB,EAAiBY,GAC/CK,EAAoB,CACtBC,oBAAqB,EACrBC,WAAY,EACZX,cAAe,EACfY,eAAgB,EAChBC,eAAgB,EAChBC,sBAAuB,EACvBC,gBAAiB,GACjBC,gBAAiB,GACjBC,uBAAwB,GACxBC,YAAa,IAGjB,GAAqB,IAAjBrB,EAAMI,OACN,OAAOF,EAAQU,GAmFnB,IAhFA,IAAIU,GAAa,EACbC,EAAmB,EAEjBC,EAAc,SAACC,EAAUC,GAI3B,GAHAd,EAAOS,YAAYK,GAASD,EAC5Bb,EAAOM,gBAAgBS,KAAKD,GAC5Bd,EAAOI,kBACoB,IAAvBP,EAAUZ,SAGV,OAFAe,EAAOC,mBAAqBa,EAC5BJ,GAAa,EACNd,EAAOI,GAElBgB,EAASF,EACb,EAiCME,EAAW,SAACF,GAEd,IAAmB,IAAfJ,EAAJ,CASA,GALAV,EAAOE,aACPF,EAAOC,mBAAqBa,EACc,mBAA/BjB,EAAUoB,kBACjBpB,EAAUoB,iBAAiBjB,GAE3BA,EAAOE,aAAed,EAAMI,OAC5B,OAAOF,EAAQU,GAEfW,EAAmBvB,EAAMI,QACzB0B,EAASP,IAXZ,CAaL,EAEMO,EAAW,SAACJ,GAEdjB,EAAUX,UAAUc,EAAQZ,GAAO+B,MAAK,SAACC,IACV,IAAvBA,EArDQ,SAACN,GAEjB,GADAd,EAAOT,gBACqB,mBAAjBH,EAAM0B,GACb,IACI1B,EAAM0B,KAASK,MACX,SAACE,GACGrB,EAAOS,YAAYK,GAASO,EAC5BrB,EAAOO,gBAAgBQ,KAAKD,GAC5Bd,EAAOG,iBACPa,EAASF,EACZ,IACD,SAACD,GACGD,EAAYC,EAAOC,EACvB,GAEP,CAAC,MAAOQ,GACLV,EAAYU,EAAUR,EACzB,KACE,KAAwC,IAApCjB,EAAUJ,sBAOjB,OADAiB,GAAa,EACNd,EACH,IAAI2B,MAAM,SAAWT,EAAQ,MAAQ1B,EAAM0B,GAAS,yCAPxDd,EAAOS,YAAYK,GAAS1B,EAAM0B,GAClCd,EAAOO,gBAAgBQ,KAAKD,GAC5Bd,EAAOG,iBACPa,EAASF,EAMZ,CACL,CA0BYU,CAAYV,IAEZd,EAAOK,wBACPL,EAAOQ,uBAAuBO,KAAKD,GACnCE,EAASF,GAEhB,GAAElB,EACP,EAGS6B,EAAI,EAAGA,EAAIC,KAAKC,IAAI9B,EAAUb,cAAeI,EAAMI,QAASiC,IACjEP,EAASP,IAEjB,GACJ,CAQA,SAASiB,EAAcxC,EAAiBO,GACpC,OAAO,IAAIN,SAAa,SAACC,EAASM,GAC9BF,EAAIN,EAAOO,GAASwB,MAChB,SAACnB,GACGV,EAAQU,EAAOS,YAClB,IACD,SAACI,GACOA,aAAiBU,MACjB3B,EAAOiB,GAEPjB,EAAOiB,EAAMJ,YAAYI,EAAMP,gBAAgB,IAEvD,GAER,GACJ,CAQgB,SAAAuB,EAAQzC,EAAiBO,GAErC,OAAOiC,EAAWxC,EADAU,OAAOC,OAAO,GAAI,CAACf,cAAe,EAAGC,UAAU,GAAOU,GAE5E,CAQgB,SAAAmC,EAAO1C,EAAiBO,GAEpC,OAAOiC,EAAWxC,EADAU,OAAOC,OAAO,CAAA,EAAI,CAACd,UAAU,GAAOU,GAE1D"}