170 lines
4.5 KiB
JavaScript
170 lines
4.5 KiB
JavaScript
'use strict';
|
|
|
|
const array = require('./array.cjs');
|
|
const typed = require('./typed.cjs');
|
|
|
|
const reduce = async (array, asyncReducer, initValue) => {
|
|
const initProvided = initValue !== void 0;
|
|
if (!initProvided && array?.length < 1) {
|
|
throw new Error("Cannot reduce empty array with no init value");
|
|
}
|
|
const iter = initProvided ? array : array.slice(1);
|
|
let value = initProvided ? initValue : array[0];
|
|
for (const [i, item] of iter.entries()) {
|
|
value = await asyncReducer(value, item, i);
|
|
}
|
|
return value;
|
|
};
|
|
const map = async (array, asyncMapFunc) => {
|
|
if (!array)
|
|
return [];
|
|
let result = [];
|
|
let index = 0;
|
|
for (const value of array) {
|
|
const newValue = await asyncMapFunc(value, index++);
|
|
result.push(newValue);
|
|
}
|
|
return result;
|
|
};
|
|
const defer = async (func) => {
|
|
const callbacks = [];
|
|
const register = (fn, options) => callbacks.push({
|
|
fn,
|
|
rethrow: options?.rethrow ?? false
|
|
});
|
|
const [err, response] = await tryit(func)(register);
|
|
for (const { fn, rethrow } of callbacks) {
|
|
const [rethrown] = await tryit(fn)(err);
|
|
if (rethrown && rethrow)
|
|
throw rethrown;
|
|
}
|
|
if (err)
|
|
throw err;
|
|
return response;
|
|
};
|
|
class AggregateError extends Error {
|
|
constructor(errors = []) {
|
|
super();
|
|
const name = errors.find((e) => e.name)?.name ?? "";
|
|
this.name = `AggregateError(${name}...)`;
|
|
this.message = `AggregateError with ${errors.length} errors`;
|
|
this.stack = errors.find((e) => e.stack)?.stack ?? this.stack;
|
|
this.errors = errors;
|
|
}
|
|
}
|
|
const parallel = async (limit, array$1, func) => {
|
|
const work = array$1.map((item, index) => ({
|
|
index,
|
|
item
|
|
}));
|
|
const processor = async (res) => {
|
|
const results2 = [];
|
|
while (true) {
|
|
const next = work.pop();
|
|
if (!next)
|
|
return res(results2);
|
|
const [error, result] = await tryit(func)(next.item);
|
|
results2.push({
|
|
error,
|
|
result,
|
|
index: next.index
|
|
});
|
|
}
|
|
};
|
|
const queues = array.list(1, limit).map(() => new Promise(processor));
|
|
const itemResults = await Promise.all(queues);
|
|
const [errors, results] = array.fork(
|
|
array.sort(itemResults.flat(), (r) => r.index),
|
|
(x) => !!x.error
|
|
);
|
|
if (errors.length > 0) {
|
|
throw new AggregateError(errors.map((error) => error.error));
|
|
}
|
|
return results.map((r) => r.result);
|
|
};
|
|
async function all(promises) {
|
|
const entries = typed.isArray(promises) ? promises.map((p) => [null, p]) : Object.entries(promises);
|
|
const results = await Promise.all(
|
|
entries.map(
|
|
([key, value]) => value.then((result) => ({ result, exc: null, key })).catch((exc) => ({ result: null, exc, key }))
|
|
)
|
|
);
|
|
const exceptions = results.filter((r) => r.exc);
|
|
if (exceptions.length > 0) {
|
|
throw new AggregateError(exceptions.map((e) => e.exc));
|
|
}
|
|
if (typed.isArray(promises)) {
|
|
return results.map((r) => r.result);
|
|
}
|
|
return results.reduce(
|
|
(acc, item) => ({
|
|
...acc,
|
|
[item.key]: item.result
|
|
}),
|
|
{}
|
|
);
|
|
}
|
|
const retry = async (options, func) => {
|
|
const times = options?.times ?? 3;
|
|
const delay = options?.delay;
|
|
const backoff = options?.backoff ?? null;
|
|
for (const i of array.range(1, times)) {
|
|
const [err, result] = await tryit(func)((err2) => {
|
|
throw { _exited: err2 };
|
|
});
|
|
if (!err)
|
|
return result;
|
|
if (err._exited)
|
|
throw err._exited;
|
|
if (i === times)
|
|
throw err;
|
|
if (delay)
|
|
await sleep(delay);
|
|
if (backoff)
|
|
await sleep(backoff(i));
|
|
}
|
|
return void 0;
|
|
};
|
|
const sleep = (milliseconds) => {
|
|
return new Promise((res) => setTimeout(res, milliseconds));
|
|
};
|
|
const tryit = (func) => {
|
|
return (...args) => {
|
|
try {
|
|
const result = func(...args);
|
|
if (typed.isPromise(result)) {
|
|
return result.then((value) => [void 0, value]).catch((err) => [err, void 0]);
|
|
}
|
|
return [void 0, result];
|
|
} catch (err) {
|
|
return [err, void 0];
|
|
}
|
|
};
|
|
};
|
|
const guard = (func, shouldGuard) => {
|
|
const _guard = (err) => {
|
|
if (shouldGuard && !shouldGuard(err))
|
|
throw err;
|
|
return void 0;
|
|
};
|
|
const isPromise2 = (result) => result instanceof Promise;
|
|
try {
|
|
const result = func();
|
|
return isPromise2(result) ? result.catch(_guard) : result;
|
|
} catch (err) {
|
|
return _guard(err);
|
|
}
|
|
};
|
|
|
|
exports.AggregateError = AggregateError;
|
|
exports.all = all;
|
|
exports.defer = defer;
|
|
exports.guard = guard;
|
|
exports.map = map;
|
|
exports.parallel = parallel;
|
|
exports.reduce = reduce;
|
|
exports.retry = retry;
|
|
exports.sleep = sleep;
|
|
exports.tryit = tryit;
|
|
//# sourceMappingURL=async.cjs.map
|