@ayonli/jsext
Advanced tools
Comparing version 0.1.0 to 0.1.1
144
function.js
@@ -18,5 +18,5 @@ "use strict"; | ||
}; | ||
function handleTry(res) { | ||
function handleTry(returns) { | ||
// Implementation details should be ordered from complex to simple. | ||
if ((0, check_iterable_1.isAsyncGenerator)(res)) { | ||
if ((0, check_iterable_1.isAsyncGenerator)(returns)) { | ||
return (async function* () { | ||
@@ -29,3 +29,3 @@ let input; | ||
try { | ||
let { done, value } = await res.next(input); | ||
let { done, value } = await returns.next(input); | ||
if (done) { | ||
@@ -53,3 +53,3 @@ result = value; | ||
} | ||
else if ((0, check_iterable_1.isGenerator)(res)) { | ||
else if ((0, check_iterable_1.isGenerator)(returns)) { | ||
return (function* () { | ||
@@ -60,3 +60,3 @@ let input; | ||
try { | ||
let { done, value } = res.next(input); | ||
let { done, value } = returns.next(input); | ||
if (done) { | ||
@@ -78,16 +78,8 @@ result = value; | ||
} | ||
else if (typeof (res === null || res === void 0 ? void 0 : res.then) === "function") { | ||
res = res.then((value) => [null, value]); | ||
// There is no guarantee that a promise-like object's `then()` | ||
// method will always return a promise, to avoid any trouble, we | ||
// need to do one more check. | ||
if (typeof (res === null || res === void 0 ? void 0 : res.catch) === "function") { | ||
return res.catch((err) => [err, undefined]); | ||
} | ||
else { | ||
return res; | ||
} | ||
else if (typeof (returns === null || returns === void 0 ? void 0 : returns.then) === "function") { | ||
returns = returns.then((value) => [null, value]); | ||
return Promise.resolve(returns).catch((err) => [err, undefined]); | ||
} | ||
else { | ||
return [null, res]; | ||
return [null, returns]; | ||
} | ||
@@ -108,2 +100,120 @@ } | ||
}; | ||
Function.useDefer = function (fn) { | ||
return function (...args) { | ||
var _a; | ||
const callbacks = []; | ||
const defer = (cb) => void callbacks.push(cb); | ||
let result; | ||
try { | ||
const returns = fn.call(this, defer, ...args); | ||
if ((0, check_iterable_1.isAsyncGenerator)(returns)) { | ||
const gen = (async function* () { | ||
var _a; | ||
let input; | ||
// Use `while` loop instead of `for...of...` in order to | ||
// retrieve the return value of a generator function. | ||
while (true) { | ||
try { | ||
let { done, value } = await returns.next(input); | ||
if (done) { | ||
result = { value, error: null }; | ||
break; | ||
} | ||
else { | ||
// Receive any potential input value that passed | ||
// to the outer `next()` call, and pass them to | ||
// `res.next()` in the next call. | ||
input = yield Promise.resolve(value); | ||
} | ||
} | ||
catch (error) { | ||
// If any error occurs, capture that error and break | ||
// the loop immediately, indicating the process is | ||
// forced broken. | ||
result = { value: void 0, error }; | ||
break; | ||
} | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
await ((_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks)); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} | ||
else { | ||
return Promise.resolve(result.value); | ||
} | ||
})(); | ||
return gen; | ||
} | ||
else if ((0, check_iterable_1.isGenerator)(returns)) { | ||
const gen = (function* () { | ||
var _a; | ||
let input; | ||
while (true) { | ||
try { | ||
let { done, value } = returns.next(input); | ||
if (done) { | ||
result = { value, error: null }; | ||
break; | ||
} | ||
else { | ||
input = yield value; | ||
} | ||
} | ||
catch (error) { | ||
result = { value: void 0, error }; | ||
break; | ||
} | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
(_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} | ||
else { | ||
return result.value; | ||
} | ||
})(); | ||
return gen; | ||
} | ||
else if (typeof (returns === null || returns === void 0 ? void 0 : returns.then) === "function") { | ||
return Promise.resolve(returns).then(value => ({ | ||
value, | ||
error: null, | ||
})).catch((error) => ({ | ||
value: void 0, | ||
error, | ||
})).then(async (result) => { | ||
var _a; | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
await ((_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks)); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} | ||
else { | ||
return result.value; | ||
} | ||
}); | ||
} | ||
else { | ||
result = { value: returns, error: null }; | ||
} | ||
} | ||
catch (error) { | ||
result = { value: void 0, error }; | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
(_a = callbacks[i]) === null || _a === void 0 ? void 0 : _a.call(callbacks); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} | ||
else { | ||
return result.value; | ||
} | ||
}; | ||
}; | ||
//# sourceMappingURL=function.js.map |
189
function.ts
import { isGenerator, isAsyncGenerator } from "check-iterable"; | ||
export { }; | ||
declare global { | ||
@@ -11,5 +9,5 @@ interface FunctionConstructor { | ||
*/ | ||
wrap<Fn extends (...args: any[]) => any>( | ||
wrap<T, Fn extends (this: T, ...args: any[]) => any>( | ||
fn: Fn, | ||
wrapper: (this: Function, fn: Fn, ...args: Parameters<Fn>) => ReturnType<Fn> | ||
wrapper: (this: T, fn: Fn, ...args: Parameters<Fn>) => ReturnType<Fn> | ||
): Fn; | ||
@@ -45,2 +43,24 @@ /** | ||
try<E = Error, R = any>(job: Promise<R>): Promise<[E, R]>; | ||
/** | ||
* Inspired by Golang, creates a function that receives a `defer` function which can be used | ||
* to carry deferred jobs that will be run after the main function is complete. | ||
* | ||
* Multiple calls of the `defer` function is supported, and the callbacks are called in the | ||
* LIFO order. Callbacks can be async functions if the main function is an async function or | ||
* an async generator function, and all the running procedures will be awaited. | ||
* | ||
* @example | ||
* const getVersion = await Function.useDefer(async (defer) => { | ||
* const file = await fs.open("./package.json", "r"); | ||
* defer(() => file.close()); | ||
* | ||
* const content = await file.readFile("utf8"); | ||
* const pkg = JSON.parse(content); | ||
* | ||
* return pkg.version as string; | ||
* }); | ||
*/ | ||
useDefer<T, R = any, A extends any[] = any[]>( | ||
fn: (this: T, defer: (cb: () => void) => void, ...args: A) => R | ||
): (this: T, ...args: A) => R; | ||
} | ||
@@ -73,8 +93,8 @@ } | ||
function handleTry(res: any) { | ||
function handleTry(returns: any) { | ||
// Implementation details should be ordered from complex to simple. | ||
if (isAsyncGenerator(res)) { | ||
if (isAsyncGenerator(returns)) { | ||
return (async function* () { | ||
let input: any; | ||
let input: unknown; | ||
let result: any; | ||
@@ -86,6 +106,3 @@ | ||
try { | ||
let { | ||
done, | ||
value | ||
} = await (<AsyncIterableIterator<any>>res).next(input); | ||
let { done, value } = await returns.next(input); | ||
@@ -111,6 +128,6 @@ if (done) { | ||
return Promise.resolve([null, result]); | ||
})() as AsyncIterableIterator<any>; | ||
} else if (isGenerator(res)) { | ||
})() as AsyncGenerator<unknown, any, unknown>; | ||
} else if (isGenerator(returns)) { | ||
return (function* () { | ||
let input: any; | ||
let input: unknown; | ||
let result: any; | ||
@@ -120,6 +137,3 @@ | ||
try { | ||
let { | ||
done, | ||
value | ||
} = (<IterableIterator<any>>res).next(input); | ||
let { done, value } = returns.next(input); | ||
@@ -139,16 +153,8 @@ if (done) { | ||
return [null, result]; | ||
})() as IterableIterator<any>; | ||
} else if (typeof res?.then === "function") { | ||
res = res.then((value: any) => [null, value]); | ||
// There is no guarantee that a promise-like object's `then()` | ||
// method will always return a promise, to avoid any trouble, we | ||
// need to do one more check. | ||
if (typeof res?.catch === "function") { | ||
return res.catch((err: any) => [err, undefined]); | ||
} else { | ||
return res; | ||
} | ||
})() as Generator<unknown, any, unknown>; | ||
} else if (typeof returns?.then === "function") { | ||
returns = (returns as PromiseLike<any>).then((value: any) => [null, value]); | ||
return Promise.resolve(returns).catch((err: unknown) => [err, undefined]) as any; | ||
} else { | ||
return [null, res]; | ||
return [null, returns]; | ||
} | ||
@@ -168,1 +174,122 @@ } | ||
}; | ||
Function.useDefer = function <E, R, A extends any[]>( | ||
fn: (defer: (cb: () => void) => void, ...args: A) => any | ||
) { | ||
return function (this: any, ...args: A) { | ||
const callbacks: (() => void)[] = []; | ||
const defer = (cb: () => void) => void callbacks.push(cb); | ||
type Result = { value?: Awaited<R>; error: E | null; }; | ||
let result: Result | undefined; | ||
try { | ||
const returns = fn.call(this, defer, ...args) as any; | ||
if (isAsyncGenerator(returns)) { | ||
const gen = (async function* () { | ||
let input: unknown; | ||
// Use `while` loop instead of `for...of...` in order to | ||
// retrieve the return value of a generator function. | ||
while (true) { | ||
try { | ||
let { done, value } = await returns.next(input); | ||
if (done) { | ||
result = { value, error: null }; | ||
break; | ||
} else { | ||
// Receive any potential input value that passed | ||
// to the outer `next()` call, and pass them to | ||
// `res.next()` in the next call. | ||
input = yield Promise.resolve(value); | ||
} | ||
} catch (error) { | ||
// If any error occurs, capture that error and break | ||
// the loop immediately, indicating the process is | ||
// forced broken. | ||
result = { value: void 0, error } as Result; | ||
break; | ||
} | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
await (callbacks[i] as () => void | Promise<void>)?.(); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} else { | ||
return Promise.resolve(result.value); | ||
} | ||
})() as AsyncGenerator<unknown, any, unknown>; | ||
return gen as R; | ||
} else if (isGenerator(returns)) { | ||
const gen = (function* () { | ||
let input: unknown; | ||
while (true) { | ||
try { | ||
let { done, value } = returns.next(input); | ||
if (done) { | ||
result = { value, error: null }; | ||
break; | ||
} else { | ||
input = yield value; | ||
} | ||
} catch (error) { | ||
result = { value: void 0, error } as Result; | ||
break; | ||
} | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
callbacks[i]?.(); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} else { | ||
return result.value; | ||
} | ||
})() as Generator<unknown, R, unknown>; | ||
return gen as R; | ||
} else if (typeof returns?.then === "function") { | ||
return Promise.resolve(returns as PromiseLike<R>).then(value => ({ | ||
value, | ||
error: null, | ||
} as Result)).catch((error: unknown) => ({ | ||
value: void 0, | ||
error, | ||
} as Result)).then(async result => { | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
await (callbacks[i] as () => void | Promise<void>)?.(); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} else { | ||
return result.value; | ||
} | ||
}) as R; | ||
} else { | ||
result = { value: returns, error: null } as Result; | ||
} | ||
} catch (error) { | ||
result = { value: void 0, error } as Result; | ||
} | ||
for (let i = callbacks.length - 1; i >= 0; i--) { | ||
callbacks[i]?.(); | ||
} | ||
if (result.error) { | ||
throw result.error; | ||
} else { | ||
return result.value as R; | ||
} | ||
}; | ||
}; |
{ | ||
"name": "@ayonli/jsext", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"description": "Additional functions added to JavaScript standard types that are frequently used in practice.", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -87,3 +87,3 @@ # JsExt | ||
- `Function` | ||
- `wrap<Fn extends (...args: any[]) => any>(fn: Fn, wrapper: (this: Function, fn: Fn, ...args: Parameters<Fn>) => ReturnType<Fn>): Fn` | ||
- `wrap<T, Fn extends (this: T, ...args: any[]) => any>(fn: Fn, wrapper: (this: T, fn: Fn, ...args: Parameters<Fn>) => ReturnType<Fn>): Fn` | ||
- `try<E = Error, T = any, A extends any[] = any[], TReturn = any, TNext = unknown>(fn: (...args: A) => AsyncGenerator<T, TReturn, TNext>, ...args: A): AsyncGenerator<[E, T], [E, TReturn], TNext>` | ||
@@ -96,2 +96,3 @@ - `try<E = Error, T = any, A extends any[] = any[], TReturn = any, TNext = unknown>(fn: (...args: A) => Generator<T, TReturn, TNext>, ...args: A): Generator<[E, T], [E, TReturn], TNext>` | ||
- `try<E = Error, R = any>(job: Promise<R>): Promise<[E, R]>` | ||
- `useDefer<T, R = any, A extends any[] = any[]>(fn: (this: T, defer: (cb: () => void) => void, ...args: A) => R): (this: T, ...args: A) => R` | ||
@@ -98,0 +99,0 @@ - `Math` |
Sorry, the diff of this file is not supported yet
83676
1577
126