From 3ebae51d4ff846431a5638993a62f6c62b8bd86e Mon Sep 17 00:00:00 2001 From: Kacper Kula Date: Fri, 19 Sep 2025 18:14:25 +0100 Subject: [PATCH] fix: fixing issue with async dependencies --- .changeset/tiny-teams-end.md | 5 +++ packages/dity/src/new-version.test.ts | 55 +++++++++++++++++++++++++++ packages/dity/src/new-version.ts | 5 ++- 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 .changeset/tiny-teams-end.md diff --git a/.changeset/tiny-teams-end.md b/.changeset/tiny-teams-end.md new file mode 100644 index 0000000..d048894 --- /dev/null +++ b/.changeset/tiny-teams-end.md @@ -0,0 +1,5 @@ +--- +'@hypersphere/dity': patch +--- + +fixing issue with function being called multiple times when it's dependencies are async diff --git a/packages/dity/src/new-version.test.ts b/packages/dity/src/new-version.test.ts index c845946..6db3962 100644 --- a/packages/dity/src/new-version.test.ts +++ b/packages/dity/src/new-version.test.ts @@ -286,4 +286,59 @@ describe('Dity', () => { .build() expect(main.get('ex.fn')).toEqual(10) }) + + it('should properly cache value of fn() even when linked from other places - sync', () => { + const fn = jest.fn(() => 5) + const ex = new Registrator() + .register('fn', d => d.fn(fn).inject()) + .export('fn') + + const mod = new Registrator() + .module('ex', ex) + .register('a', d => d.fn((a: number) => 2 * a).inject('ex.fn')) + .import<'aaa', number>() + .import<'bbb', number>() + .link('aaa', 'ex.fn') + .link('bbb', 'a') + .build() + + expect(fn).not.toHaveBeenCalled() + + expect(mod.get('bbb')).toEqual(10) + expect(fn).toHaveBeenCalledTimes(1) + mod.get('ex.fn') + expect(fn).toHaveBeenCalledTimes(1) + + mod.get('bbb') + expect(fn).toHaveBeenCalledTimes(1) + + mod.get('aaa') + expect(fn).toHaveBeenCalledTimes(1) + }) + + it('should properly cache value of fn() when it is being requested async', async () => { + const delay = (n: number) => new Promise(resolve => setTimeout(resolve, n)) + const fn = jest.fn(async (a: number, b: number) => { await delay(200); return Promise.resolve(a + b)}) + const mod = new Registrator() + .register('x', d => d.fn(async () => { await delay(100); return 40}).inject()) + .register('y', d => d.fn(async () => { await delay(100); return 60 }).inject()) + .register('a', d => d.fn(fn).inject('x', 'y')) + .register('b', 10) + .import<'iA', Promise>() + .register('c', d => d.fn((a: number, b: number) => a + b).inject('a', 'b')) + .register('d', d => d.fn((a: number, b: number) => a + b).inject('c', 'iA')) + .register('e', d => d.fn((a: number, b: number) => a + b).inject('d', 'c')) + .register('f', d => d.fn((a: number, b: number) => a + b).inject('e', 'iA')) + .link('iA', 'c') + .build() + + expect(fn).not.toHaveBeenCalled() + + const promises = Promise.all([mod.get('f'), mod.get('f'), mod.get('iA')]) + await delay(100) + await promises + + expect(await mod.get('f')).toEqual(440) + expect(fn).toHaveBeenCalledTimes(1) + }) }) diff --git a/packages/dity/src/new-version.ts b/packages/dity/src/new-version.ts index a36df91..1166aab 100644 --- a/packages/dity/src/new-version.ts +++ b/packages/dity/src/new-version.ts @@ -75,7 +75,10 @@ class Inj, Ret> { const res = this.args.map(a => d.get(a)) const anyPromise = res.find(f => (f as any) instanceof Promise) if (anyPromise) { - return Promise.all(res).then(vals => savedFn(...(vals as any))) + v = Promise.all(res).then(vals => { + return savedFn(...(vals as any)) + }) as Ret + return v } else { v = savedFn(...(res as Arr)) return v