From 370d8f0ee3b35a59b340e619f1e3d68695a55fc3 Mon Sep 17 00:00:00 2001 From: Ge Gao Date: Mon, 19 Oct 2026 05:00:15 -0700 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9E=20fix(all):=20Resume=20ready=20eff?= =?UTF-8?q?ects=20at=20index=20zero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/effected.ts | 4 ++-- test/effected.spec.ts | 55 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/effected.ts b/src/effected.ts index 3b1bc99..40dd549 100644 --- a/src/effected.ts +++ b/src/effected.ts @@ -497,7 +497,7 @@ export class Effected implements Iterable implements Iterable { describe("Effected.all", () => { const delay = effect("delay")<[ms: number, label: string], string>; + function deferred() { + let resolve!: (value: T | PromiseLike) => void; + const promise = new Promise((complete) => { + resolve = complete; + }); + return { promise, resolve }; + } + it("should run effects in parallel rather than sequentially", async () => { const executeOrder: string[] = []; const startTime = Date.now(); @@ -1382,6 +1390,53 @@ describe("Effected.all", () => { expect(seqResult).toEqual(result); }); + it("should complete nested effects resolved in reverse order in the same turn", async () => { + const first = deferred(); + const second = deferred(); + const firstTask = effected(function* () { + return yield* effectify(first.promise); + }); + const secondTask = effected(function* () { + return yield* effectify(second.promise); + }); + const result = Effected.all([Effected.all([firstTask, secondTask])]).runAsync(); + + second.resolve("second"); + first.resolve("first"); + + await expect(result).resolves.toEqual([["first", "second"]]); + }); + + it("should complete the ready first effect while the second effect waits again", async () => { + const first = deferred(); + const second = deferred(); + const third = deferred(); + const firstCompleted = deferred(); + const secondWaiting = deferred(); + + const firstTask = effected(function* () { + const value = yield* effectify(first.promise); + firstCompleted.resolve(value); + return value; + }); + const secondTask = effected(function* () { + const value = yield* effectify(second.promise); + secondWaiting.resolve(); + yield* effectify(third.promise); + return value; + }); + const result = Effected.all([Effected.all([firstTask, secondTask])]).runAsync(); + + second.resolve("second"); + first.resolve("first"); + + await secondWaiting.promise; + await expect(firstCompleted.promise).resolves.toBe("first"); + third.resolve(); + + await expect(result).resolves.toEqual([["first", "second"]]); + }); + it("should handle empty input correctly", () => { expect(Effected.all([]).runSync()).toEqual([]); expect(Effected.all({}).runSync()).toEqual({});