From b88fdb40a9a64edb12c9d3372900c6bfdfcd1c12 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 29 May 2024 11:14:05 +0200 Subject: [PATCH] [CL-298] feat: add duplicate execution protection --- .../src/async-actions/async-actions.service.spec.ts | 12 +++++++++++- .../src/async-actions/async-actions.service.ts | 10 +++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/libs/components/src/async-actions/async-actions.service.spec.ts b/libs/components/src/async-actions/async-actions.service.spec.ts index 52e77f7ded3..e9c26e4646d 100644 --- a/libs/components/src/async-actions/async-actions.service.spec.ts +++ b/libs/components/src/async-actions/async-actions.service.spec.ts @@ -126,7 +126,17 @@ describe("AsyncActionsService", () => { expect(action.observed).toBe(false); }); - it("ignores EmptyError and completes the action", async () => {}); + it("does not execute the handler if another action is already active in the same context", async () => { + const first_action = new Subject(); + const second_action = new Subject(); + const origin = Symbol(); + + void service.execute("context", origin, () => first_action); + void service.execute("context", origin, () => second_action); + + expect(first_action.observed).toBe(true); + expect(second_action.observed).toBe(false); + }); }); }); diff --git a/libs/components/src/async-actions/async-actions.service.ts b/libs/components/src/async-actions/async-actions.service.ts index b67675d8207..1dd29e58a23 100644 --- a/libs/components/src/async-actions/async-actions.service.ts +++ b/libs/components/src/async-actions/async-actions.service.ts @@ -57,17 +57,25 @@ export class AsyncActionsService implements OnDestroy { * - Regular functions are also supported, but the loading state will not be set. This is useful for functions that might * need to return early. * + * NOTE: The handler will not be executed if another action is already active in the same context. + * * @param context A string that will be used to group the loading state of multiple async actions. * @param origin The object that the action originated from. * @param handler The function to execute. * @param until An observable that will cause the action to be cancelled when it emits. */ async execute( - context: string | undefined, + context: string, origin: unknown, handler: FunctionReturningAwaitable, until?: Observable, ): Promise { + // Access the current state directly, otherwise you can execute multiple actions in the same context + // by not awaiting the promise returned by this method. + if (this.states$.value?.[context]?.status === "active") { + return; + } + this.updateState((state) => ({ ...state, [context]: { status: "active", origin } })); try {