diff --git a/packages/memoize/src/memoize.ts b/packages/memoize/src/memoize.ts
index 4880b42918..05c4878cc0 100644
--- a/packages/memoize/src/memoize.ts
+++ b/packages/memoize/src/memoize.ts
@@ -1,4 +1,4 @@
-import type { Fn, Fn2, Fn3, Fn4, FnAny } from "@thi.ng/api";
+import type { FnAny } from "@thi.ng/api";
import type { MapLike } from "./api.js";
/**
@@ -18,20 +18,11 @@ import type { MapLike } from "./api.js";
* @param fn -
* @param cache -
*/
-export function memoize(fn: Fn, cache: MapLike): Fn;
-export function memoize(
- fn: Fn2,
- cache: MapLike<[A, B], C>
-): Fn2;
-export function memoize(
- fn: Fn3,
- cache: MapLike<[A, B, C], D>
-): Fn3;
-export function memoize(
- fn: Fn4,
- cache: MapLike<[A, B, C, D], E>
-): Fn4;
-export function memoize(fn: FnAny, cache: MapLike): FnAny {
+export function memoize>(
+ fn: T,
+ cache: MapLike
+): T {
+ // @ts-ignore
return (...args: any[]) => {
let res;
return cache.has(args)
@@ -39,3 +30,22 @@ export function memoize(fn: FnAny, cache: MapLike): FnAny {
: (cache.set(args, (res = fn.apply(null, args))), res);
};
}
+
+/**
+ * Async version of {@link memoize}.
+ *
+ * @param fn
+ * @param cache
+ */
+export function memoizeAsync>(
+ fn: T,
+ cache: MapLike
+): (...xs: Parameters) => Promise>> {
+ // @ts-ignore
+ return async (...args: any[]) => {
+ let res;
+ return cache.has(args)
+ ? cache.get(args)
+ : (cache.set(args, (res = await fn.apply(null, args))), res);
+ };
+}
diff --git a/packages/memoize/src/memoize1.ts b/packages/memoize/src/memoize1.ts
index 105f6d13bd..80cbf8c0f3 100644
--- a/packages/memoize/src/memoize1.ts
+++ b/packages/memoize/src/memoize1.ts
@@ -1,4 +1,4 @@
-import type { Fn } from "@thi.ng/api";
+import type { Fn, MaybePromise } from "@thi.ng/api";
import type { MapLike } from "./api.js";
/**
@@ -24,3 +24,18 @@ export const memoize1 =
? cache.get(x)!
: (cache.set(x, (res = fn(x))), res);
};
+
+/**
+ * Async version of {@link memoize1}.
+ *
+ * @param fn
+ * @param cache
+ */
+export const memoizeAsync1 =
+ (fn: Fn>, cache: MapLike = new Map()) =>
+ async (x: A): Promise => {
+ let res;
+ return cache.has(x)
+ ? cache.get(x)!
+ : (cache.set(x, (res = await fn(x))), res);
+ };
diff --git a/packages/memoize/src/memoizej.ts b/packages/memoize/src/memoizej.ts
index fc1cd250ce..dff1d527d7 100644
--- a/packages/memoize/src/memoizej.ts
+++ b/packages/memoize/src/memoizej.ts
@@ -1,4 +1,4 @@
-import type { Fn, Fn2, Fn3, Fn4, FnAny, IObjectOf } from "@thi.ng/api";
+import type { FnAny, IObjectOf } from "@thi.ng/api";
/**
* Function memoization for arbitrary argument counts. Returns augmented
@@ -15,23 +15,11 @@ import type { Fn, Fn2, Fn3, Fn4, FnAny, IObjectOf } from "@thi.ng/api";
* @param fn -
* @param cache -
*/
-export function memoizeJ(fn: Fn, cache?: IObjectOf): Fn;
-export function memoizeJ(
- fn: Fn2,
- cache?: IObjectOf
-): Fn2;
-export function memoizeJ(
- fn: Fn3,
- cache?: IObjectOf
-): Fn3;
-export function memoizeJ(
- fn: Fn4,
- cache?: IObjectOf
-): Fn4;
-export function memoizeJ(
- fn: FnAny,
- cache: Record = Object.create(null)
-): FnAny {
+export function memoizeJ>(
+ fn: T,
+ cache: IObjectOf = Object.create(null)
+): T {
+ // @ts-ignore
return (...args: any[]) => {
const key = JSON.stringify(args);
if (key !== undefined) {
@@ -42,3 +30,25 @@ export function memoizeJ(
return fn.apply(null, args);
};
}
+
+/**
+ * Async version of {@link memoizeJ}.
+ *
+ * @param fn
+ * @param cache
+ */
+export function memoizeAsyncJ>(
+ fn: T,
+ cache: IObjectOf = Object.create(null)
+): (...xs: Parameters) => Promise>> {
+ // @ts-ignore
+ return async (...args: any[]) => {
+ const key = JSON.stringify(args);
+ if (key !== undefined) {
+ return key in cache
+ ? cache[key]
+ : (cache[key] = await fn.apply(null, args));
+ }
+ return await fn.apply(null, args);
+ };
+}
diff --git a/packages/memoize/src/memoizeo.ts b/packages/memoize/src/memoizeo.ts
index 025cea61f1..ea21b1bb55 100644
--- a/packages/memoize/src/memoizeo.ts
+++ b/packages/memoize/src/memoizeo.ts
@@ -1,12 +1,24 @@
-import type { Fn, Fn2, Fn3, Fn4, NumOrString } from "@thi.ng/api";
+import type {
+ Fn,
+ Fn2,
+ Fn3,
+ Fn4,
+ IObjectOf,
+ MaybePromise,
+ NumOrString,
+} from "@thi.ng/api";
/**
- * The most minimalistic & fastest memoization function of this package. Similar
- * to {@link memoize1}, but only supports numbers or strings as keys and uses a
- * vanilla JS object as cache.
+ * The most minimalistic memoization function of this package, but only supports
+ * numbers or strings as arguments (max. 4) and uses a vanilla JS object as
+ * cache.
*
* @remarks
- * Also see {@link memoize1}, {@link memoizeJ}, {@link memoize}.
+ * If `fn` throws an error, no result value will be cached and no memoization
+ * happens for this invocation using the given arguments.
+ *
+ * Use {@link memoizeAsyncO} for async functions or other functions returning
+ * promises.
*
* @example
* ```ts tangle:../export/memoizeo.ts
@@ -29,64 +41,82 @@ import type { Fn, Fn2, Fn3, Fn4, NumOrString } from "@thi.ng/api";
* @param fn
* @param cache
*/
-export const memoizeO =
- (
- fn: Fn,
- cache: Record = Object.create(null)
- ) =>
- (x: A): B =>
- x in cache ? cache[x] : (cache[x] = fn(x));
-
-/**
- * Like {@link memoizeO}, but for functions with 2 arguments.
- *
- * @param fn
- * @param cache
- */
-export const memoize2O =
- (
- fn: Fn2,
- cache: Record = Object.create(null)
- ) =>
- (a: A, b: B): C => {
- const key = a + "-" + b;
- return key in cache ? cache[key] : (cache[key] = fn(a, b));
+export function memoizeO(
+ fn: Fn,
+ cache?: IObjectOf
+): Fn;
+export function memoizeO(
+ fn: Fn2,
+ cache?: IObjectOf
+): Fn2;
+export function memoizeO<
+ A extends NumOrString,
+ B extends NumOrString,
+ C extends NumOrString,
+ D
+>(fn: Fn3, cache?: IObjectOf): Fn3;
+export function memoizeO<
+ A extends NumOrString,
+ B extends NumOrString,
+ C extends NumOrString,
+ D extends NumOrString,
+ E
+>(fn: Fn4, cache?: IObjectOf): Fn4;
+export function memoizeO any>(
+ fn: T,
+ cache: IObjectOf> = Object.create(null)
+): T {
+ // @ts-ignore
+ return (...xs: any[]) => {
+ const key = xs.join("-");
+ return key in cache ? cache[key] : (cache[key] = fn(...xs));
};
+}
/**
- * Like {@link memoizeO}, but for functions with 3 arguments.
+ * Async version of {@link memoizeO}.
*
- * @param fn
- * @param cache
- */
-export const memoize3O =
- (
- fn: Fn3,
- cache: Record = Object.create(null)
- ) =>
- (a: A, b: B, c: C): D => {
- const key = a + "-" + b + "-" + c;
- return key in cache ? cache[key] : (cache[key] = fn(a, b, c));
- };
-
-/**
- * Like {@link memoizeO}, but for functions with 4 arguments.
+ * @remarks
+ * If `fn` throws an error, no result value will be cached and no memoization
+ * happens for this invocation using the given arguments.
*
* @param fn
* @param cache
*/
-export const memoize4O =
- <
- A extends NumOrString,
- B extends NumOrString,
- C extends NumOrString,
- D extends NumOrString,
- E
- >(
- fn: Fn4,
- cache: Record = Object.create(null)
- ) =>
- (a: A, b: B, c: C, d: D): E => {
- const key = a + "-" + b + "-" + c + "-" + d;
- return key in cache ? cache[key] : (cache[key] = fn(a, b, c, d));
+export function memoizeAsyncO(
+ fn: Fn>,
+ cache?: IObjectOf
+): Fn>;
+export function memoizeAsyncO(
+ fn: Fn2>,
+ cache?: IObjectOf
+): Fn2>;
+export function memoizeAsyncO<
+ A extends NumOrString,
+ B extends NumOrString,
+ C extends NumOrString,
+ D
+>(
+ fn: Fn3>,
+ cache?: IObjectOf
+): Fn3>;
+export function memoizeAsyncO<
+ A extends NumOrString,
+ B extends NumOrString,
+ C extends NumOrString,
+ D extends NumOrString,
+ E
+>(
+ fn: Fn4>,
+ cache?: IObjectOf
+): Fn4>;
+export function memoizeAsyncO any>(
+ fn: T,
+ cache: IObjectOf> = Object.create(null)
+): T {
+ // @ts-ignore
+ return async (...xs: any[]) => {
+ const key = xs.join("-");
+ return key in cache ? cache[key] : (cache[key] = await fn(...xs));
};
+}
diff --git a/packages/memoize/test/main.test.ts b/packages/memoize/test/main.test.ts
index aa548208a2..3f6f3c322c 100644
--- a/packages/memoize/test/main.test.ts
+++ b/packages/memoize/test/main.test.ts
@@ -1,7 +1,7 @@
import { EquivMap } from "@thi.ng/associative";
import { LRUCache } from "@thi.ng/cache";
import { expect, test } from "bun:test";
-import { memoize1, memoize2O, memoizeO } from "../src/index.js";
+import { memoize1, memoizeO } from "../src/index.js";
test("memoize1", () => {
const calls: number[] = [];
@@ -16,7 +16,7 @@ test("memoize1", () => {
test("memoizeO", () => {
const calls: number[] = [];
- const f = memoizeO((x) => (calls.push(x), x * 10));
+ const f = memoizeO((x: number) => (calls.push(x), x * 10));
expect(f(1)).toBe(10);
expect(f(2)).toBe(20);
expect(f(2)).toBe(20);
@@ -27,8 +27,10 @@ test("memoizeO", () => {
test("memoize2O", () => {
const calls: number[][] = [];
- const f = memoize2O(
- (a, b) => (calls.push([a, b]), a * b)
+ const cache: Record = {};
+ const f = memoizeO(
+ (a: number, b: number) => (calls.push([a, b]), a * b),
+ cache
);
expect(f(1, 2)).toBe(2);
expect(f(1, 2)).toBe(2);
@@ -39,6 +41,11 @@ test("memoize2O", () => {
[2, 3],
[2, 1],
]);
+ expect(cache).toEqual({
+ "1-2": 2,
+ "2-1": 2,
+ "2-3": 6,
+ });
});
test("memoize1 (equivmap)", () => {