From 4817be49d1a2c95724898828bb0e123bb0d67b89 Mon Sep 17 00:00:00 2001 From: bjoluc Date: Wed, 8 Nov 2023 18:18:20 +0100 Subject: [PATCH] Allow trial `on_finish` methods to be async --- .changeset/old-moons-lay.md | 5 +++++ packages/jspsych/src/timeline/Trial.spec.ts | 23 ++++++++++++++++++++- packages/jspsych/src/timeline/Trial.ts | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 .changeset/old-moons-lay.md diff --git a/.changeset/old-moons-lay.md b/.changeset/old-moons-lay.md new file mode 100644 index 0000000000..5b3ffc70a9 --- /dev/null +++ b/.changeset/old-moons-lay.md @@ -0,0 +1,5 @@ +--- +"jspsych": minor +--- + +Allow trial `on_finish` methods to be asynchronous, i.e. return a `Promise`. Prior to this, promises returned by `on_finish` were not awaited before proceeding with the next trial. diff --git a/packages/jspsych/src/timeline/Trial.spec.ts b/packages/jspsych/src/timeline/Trial.spec.ts index e763948e64..83cfc29795 100644 --- a/packages/jspsych/src/timeline/Trial.spec.ts +++ b/packages/jspsych/src/timeline/Trial.spec.ts @@ -6,7 +6,7 @@ import TestPlugin from "../../tests/TestPlugin"; import { JsPsychPlugin, ParameterType } from "../modules/plugins"; import { Timeline } from "./Timeline"; import { Trial } from "./Trial"; -import { parameterPathArrayToString } from "./util"; +import { PromiseWrapper, parameterPathArrayToString } from "./util"; import { SimulationOptionsParameter, TimelineVariable, @@ -168,6 +168,27 @@ describe("Trial", () => { expect(onFinishCallback).toHaveBeenCalledWith(expect.objectContaining({ my: "result" })); }); + it("awaits async `on_finish` callbacks", async () => { + const onFinishCallbackPromise = new PromiseWrapper(); + const trial = createTrial({ + type: TestPlugin, + on_finish: () => onFinishCallbackPromise.get(), + }); + + let hasTrialCompleted = false; + trial.run().then(() => { + hasTrialCompleted = true; + }); + + await flushPromises(); + expect(hasTrialCompleted).toBe(false); + + onFinishCallbackPromise.resolve(); + await flushPromises(); + + expect(hasTrialCompleted).toBe(true); + }); + it("invokes the global `onTrialResultAvailable` and `onTrialFinished` callbacks", async () => { const invocations: string[] = []; dependencies.onTrialResultAvailable.mockImplementationOnce(() => { diff --git a/packages/jspsych/src/timeline/Trial.ts b/packages/jspsych/src/timeline/Trial.ts index bfb2f6f927..3ad079a7b3 100644 --- a/packages/jspsych/src/timeline/Trial.ts +++ b/packages/jspsych/src/timeline/Trial.ts @@ -232,7 +232,7 @@ export class Trial extends TimelineNode { ); Object.assign(this.result, extensionResults); - this.runParameterCallback("on_finish", this.getResult()); + await Promise.resolve(this.runParameterCallback("on_finish", this.getResult())); this.dependencies.onTrialFinished(this); }