UNPKG

promising-utils

Version:

A utility library for promises, support both esm and commonjs

208 lines (201 loc) 5.06 kB
// src/timeout.ts var timeout = (fn, { duration = Infinity, timeoutMsg }) => { return new Promise((resolve, reject) => { let timer = void 0; if (Number.isFinite(duration)) { timeoutMsg = timeoutMsg ?? `timed out after ${duration} milliseconds`; timer = setTimeout(() => { reject(new Error(timeoutMsg)); }, duration); } fn().then((val) => { clearTimeout(timer); resolve(val); }).catch((err) => { clearTimeout(timer); reject(err); }); }); }; // src/wait.ts var wait = (duration) => new Promise((resolve) => { setTimeout(resolve, duration); }); // src/utils.ts var isNumber = (val) => { return typeof val === "number"; }; var isUndefinedOrNull = (val) => { return val === void 0 || val === null; }; var isFunction = (val) => { return typeof val === "function"; }; // src/retry.ts var retry = (fn, { retries = Infinity, retryIf, retryUntil, delay, timeout: timeout2 } = {}) => { if (!isNumber(retries) || retries < 0) { throw new Error("retries must to be greater than or equal to 0"); } if (!isUndefinedOrNull(timeout2)) { if (!isNumber(timeout2)) { throw new Error("timeout must be a milliseconds"); } if (timeout2 < 0) { throw new Error("timeout must be greater than or equal to 0"); } } let count = 0; return new Promise((resolve, reject) => { const fnToRun = timeout2 ? () => timeout(fn, { duration: timeout2 }) : fn; const run = () => { fnToRun().then(resolve).catch(async (err) => { if (count >= retries) { return reject(err); } if (retryIf && isFunction(retryIf) && !retryIf(err, count)) { return reject(err); } if (retryUntil && isFunction(retryUntil) && retryUntil(err, count)) { return reject(err); } count++; if (delay) { if (isNumber(delay)) { await wait(delay); } else { await wait(delay(count)); } } run(); }); }; run(); }); }; // src/parallel.ts var parallel = async (arr, { concurrency = Infinity, timeout: timeout2, stopOnReject, retries = 0 } = {}) => { if (concurrency <= 0) { throw new Error("concurrency must be greater than 0"); } if (timeout2 !== void 0 && timeout2 <= 0) { throw new Error("timeout should be greater than 0"); } if (retries < 0) { throw new Error("retries should be greater than or equal to 0"); } concurrency = Math.min(arr.length, concurrency); const result = Array(arr.length).fill(void 0); let index = 0; let count = 0; return new Promise((resolve, reject) => { const runNext = () => { if (count === arr.length) { return resolve(result); } const curIndex = index; index++; if (curIndex < arr.length) { const promise = retry(arr[curIndex], { retries, timeout: timeout2 }); promise.then((value) => { count++; result[curIndex] = { status: "fulfilled", value }; runNext(); }).catch((err) => { count++; result[curIndex] = { status: "rejected", reason: err }; if (stopOnReject) { return resolve(result); } runNext(); }); } }; for (let i = 0; i < concurrency; i++) { runNext(); } }); }; // src/batch.ts var batch = async (fns, { size, delay, onBatchStart, onBatchEnd, stopOnReject, waterFall = false, waterFallInitialValue } = {}) => { if (isUndefinedOrNull(size)) { size = fns.length; } const result = []; for (let i = 0; i < fns.length; i += size) { onBatchStart == null ? void 0 : onBatchStart(i); const promises = fns.slice(i, i + size).map((fn, index) => { const lastRes = result[i + index - size]; const nextInput = waterFall ? (lastRes == null ? void 0 : lastRes.status) === "fulfilled" ? lastRes == null ? void 0 : lastRes.value : (lastRes == null ? void 0 : lastRes.reason) ?? waterFallInitialValue : void 0; return fn(nextInput); }); const res = await Promise.allSettled(promises); let hasError = false; for (const r of res) { result.push(r); if (!hasError && r.status === "rejected") { hasError = true; } } onBatchEnd == null ? void 0 : onBatchEnd(i); if (stopOnReject && hasError) { break; } if (delay) { if (isNumber(delay)) { await wait(delay); } else { await wait(delay(i)); } } } return result; }; // src/series.ts var series = async (fns, { delay, stopOnReject, waterFall, onEachStart, onEachEnd, waterFallInitialValue } = {}) => { return batch(fns, { size: 1, onBatchStart: onEachStart, onBatchEnd: onEachEnd, delay, stopOnReject, waterFall, waterFallInitialValue }); }; export { batch, parallel, retry, series, timeout, wait };