Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/effected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ export class Effected<out E extends Effect, out R> implements Iterable<E, R, unk
});
const nextIndex = nextIdleIndex();

if (!nextIndex)
if (nextIndex === null)
return {
done: false,
value: {
Expand Down Expand Up @@ -527,7 +527,7 @@ export class Effected<out E extends Effect, out R> implements Iterable<E, R, unk
return { done: true, value: results };
} else {
const nextIndex = nextIdleIndex();
if (!nextIndex)
if (nextIndex === null)
return {
done: false,
value: {
Expand Down
55 changes: 55 additions & 0 deletions test/effected.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,14 @@ describe("Default handlers", () => {
describe("Effected.all", () => {
const delay = effect("delay")<[ms: number, label: string], string>;

function deferred<T>() {
let resolve!: (value: T | PromiseLike<T>) => void;
const promise = new Promise<T>((complete) => {
resolve = complete;
});
return { promise, resolve };
}

it("should run effects in parallel rather than sequentially", async () => {
const executeOrder: string[] = [];
const startTime = Date.now();
Expand Down Expand Up @@ -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<string>();
const second = deferred<string>();
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<string>();
const second = deferred<string>();
const third = deferred<void>();
const firstCompleted = deferred<string>();
const secondWaiting = deferred<void>();

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({});
Expand Down
Loading