@jovian/type-tools
Version:
TypeTools is a Typescript library for providing extensible tooling runtime validations and type helpers.
143 lines (135 loc) • 5.02 kB
text/typescript
/* Jovian (c) 2020, License: MIT */
export class Promise2<T = any> implements Promise<T> {
pending: boolean;
value: T;
error: Error;
get [Symbol.toStringTag]() { return `Promise`; }
constructor(
executor: (resolve: (value?: T) => any, reject: (error?: Error) => any) => any,
onFinalize?: (e?: Error, value?: T) => any) {
let pending: boolean = false;
let error: Error;
let value: T;
const prom: any = (this as any)._original_promise = new Promise<T>((resolve, reject) => {
const resolveWrapper = (r?) => {
if (prom) { prom.pending = false; prom.value = r; }
pending = false; value = r;
let onFinalizeProm: Promise<any>;
if (onFinalize) {
try { onFinalizeProm = onFinalize(null, r); } catch (e2) { console.error(e2); }
}
if (onFinalizeProm?.then) {
onFinalizeProm.catch(e3 => console.error(e3)).finally(() => resolve(r));
} else {
resolve(r);
}
};
const rejectWrapper = (e) => {
if (!e) { e = new Error(`Unnamed reject`); }
if (prom) { prom.pending = false; prom.error = e; }
pending = false; error = e;
let onFinalizeProm: Promise<any>;
if (onFinalize) {
try { onFinalizeProm = onFinalize(e, null); } catch (e2) { console.error(e2); }
}
if (onFinalizeProm?.then) {
onFinalizeProm.catch(e3 => console.error(e3)).finally(() => reject(e));
} else {
reject(e);
}
};
try {
const res = executor(
resolveWrapper,
rejectWrapper,
);
if (res && res.then) { res.catch(e2 => rejectWrapper(e2)); }
} catch (e) {
rejectWrapper(e);
}
});
if (!pending) {
prom.pending = pending;
prom.value = value;
if (error) { prom.error = error; }
} else {
prom.pending = true;
}
return prom as any;
}
then<TResult1 = T, TResult2 = never>(onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2> {
return null as any;
}
catch<TResult = never>(onrejected?: (reason: any) => TResult | PromiseLike<TResult>): Promise<T | TResult> {
return null as any;
}
finally(onfinally?: () => void): Promise<T> {
return null as any;
}
}
export function promise<T = any>(
executor: (resolve: (value?: T) => any, reject: (error?: Error) => any) => any,
onFinalize?: (e?: Error, value?: T) => any
): Promise2<T> {
return new Promise2<T>(executor, onFinalize);
}
export namespace PromUtil {
export function withFinalizer<T = any>(promise: Promise<T>,
finalizer: (result: T | Error, isError?: boolean, index?: number | string) => any,
index?: number | string) {
promise.then(r => finalizer(r, false, index)).catch(e => finalizer(e, true, index));
return promise;
}
export function allSettled<T = any>(
promises: Promise<T>[],
onIndividualFinish?: <T>(result: T | Error, isError?: boolean, index?: number | string) => any,
) {
return new Promise<(T | Error)[]>(resolve => {
const results: (T | Error)[] = [];
if (promises.length === 0) { return resolve(results); }
(results as any)._onIndividualFinishErrors = [];
results.length = promises.length;
let handledCount = 0;
const finalizer = (result: T | Error, isError?: boolean, i?: number | string) => {
results[i] = result;
++handledCount;
if (onIndividualFinish) {
try {
onIndividualFinish(result, isError, i);
} catch (e) {
(results as any)._onIndividualFinishErrors.push(e);
}
}
if (handledCount >= promises.length) { resolve(results); }
};
for (let i = 0; i < promises.length; ++i) { withFinalizer(promises[i], finalizer, i); }
});
}
export interface PromiseResult<T> {
data: T;
error: Error;
}
export function allAsorted<T = any>(promises: Promise<T>[]) {
return new Promise<{ results: PromiseResult<T>[]; valids: T[]; errors: Error[]; }>(async resolve => {
const results: PromiseResult<T>[] = [];
results.length = promises.length;
const valids: T[] = [];
const errors: Error[] = [];
let handledCount = 0;
const finalizer = (result: T | Error, isError?: boolean, i?: number | string) => {
results[i] = {
data: isError ? null : result as T,
error: isError ? result as Error : null
};
++handledCount;
if (handledCount >= promises.length) {
for (const r of results) {
if (!r.error) { valids.push(r.data); } else { errors.push(r.error); }
}
resolve({ results, valids, errors });
}
};
for (let i = 0; i < promises.length; ++i) { withFinalizer(promises[i], finalizer, i); }
});
}
}