From 9c6d49d603305ef2a3cd3b693838d07ccf25e215 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 14 Mar 2024 10:50:37 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20=F0=9F=90=9B=20correctly=20store=20execu?= =?UTF-8?q?tion=20results=20in=20once=20decorator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/__tests__/once.spec.ts | 58 ++++++++++++++++++++++++++++++++++++++ src/concurrency.ts | 2 ++ src/once.ts | 14 ++++----- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/src/__tests__/once.spec.ts b/src/__tests__/once.spec.ts index c8ba3d4..4fe4a74 100644 --- a/src/__tests__/once.spec.ts +++ b/src/__tests__/once.spec.ts @@ -15,3 +15,61 @@ test('limits function invocation to one time only an memoizes the first result', expect(a.test(2)).toBe(123); expect(executions).toBe(1); }); + +test('limits function invocation to one time for two methods', async () => { + let aExec = 0; + let bExec = 0; + class A { + @once + a(value: number) { + aExec++; + return value; + } + + @once + b(value: number) { + bExec++; + return value + 1; + } + } + const a = new A(); + expect(a.a(123)).toBe(123); + expect(a.a(1)).toBe(123); + expect(a.b(1)).toBe(2); + expect(a.b(1)).toBe(2); + expect(a.b(123123)).toBe(2); + expect(a.a(2)).toBe(123); + expect(a.a(234)).toBe(123); + expect(aExec).toBe(1); + expect(bExec).toBe(1); +}); + +test('limits function invocation to one time for two methods of two classes', async () => { + let aExec = 0; + let bExec = 0; + class A { + @once + a(value: number) { + aExec++; + return value; + } + } + class B { + @once + b(value: number) { + bExec++; + return value + 1; + } + } + const a = new A(); + const b = new B(); + expect(a.a(123)).toBe(123); + expect(a.a(1)).toBe(123); + expect(b.b(1)).toBe(2); + expect(b.b(1)).toBe(2); + expect(b.b(123123)).toBe(2); + expect(a.a(2)).toBe(123); + expect(a.a(234)).toBe(123); + expect(aExec).toBe(1); + expect(bExec).toBe(1); +}); diff --git a/src/concurrency.ts b/src/concurrency.ts index caf4061..0eff2c0 100644 --- a/src/concurrency.ts +++ b/src/concurrency.ts @@ -1,6 +1,8 @@ import {go} from './go'; import type {Code} from './types'; +/* tslint:disable */ + class Task { public readonly resolve!: (data: T) => void; public readonly reject!: (error: any) => void; diff --git a/src/once.ts b/src/once.ts index 62f2f72..a69b646 100644 --- a/src/once.ts +++ b/src/once.ts @@ -3,16 +3,14 @@ * subsequent calls will return the result of the first call. */ export function once( - target: (this: This, ...args: Args) => Return, + fn: (this: This, ...args: Args) => Return, context?: ClassMethodDecoratorContext Return>, ) { - let called = false; - let res: unknown; + const instances = new WeakMap>(); return function (this: This, ...args: Args): Return { - if (!called) { - called = true; - res = target.call(this, ...args); - } - return res as Return; + if (!instances.has(this)) instances.set(this, new WeakMap()); + const map = instances.get(this)!; + if (!map.has(fn)) map.set(fn, fn.apply(this, args)); + return map.get(fn); }; }