Skip to content
Open
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
2 changes: 0 additions & 2 deletions packages/durabletask-js/src/task/composite-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import { Task } from "./task";
export class CompositeTask<T> extends Task<T> {
_tasks: Task<any>[] = [];
_completedTasks: number;
_failedTasks: number;

constructor(tasks: Task<any>[]) {
super();

this._tasks = tasks;
this._completedTasks = 0;
this._failedTasks = 0;

for (const task of tasks) {
task._parent = this;
Expand Down
4 changes: 2 additions & 2 deletions packages/durabletask-js/src/task/when-all-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ export class WhenAllTask<T> extends CompositeTask<T[]> {
constructor(tasks: Task<T>[]) {
super(tasks);

// Note: Do NOT re-initialize _completedTasks or _failedTasks here.
// CompositeTask's constructor already initializes them to 0 and then
// Note: Do NOT re-initialize _completedTasks here.
// CompositeTask's constructor already initializes it to 0 and then
// processes pre-completed children via onChildCompleted(), which
// increments the counter. Re-initializing would wipe out that count
// and cause the task to hang when some children are already complete.
Expand Down
91 changes: 0 additions & 91 deletions packages/durabletask-js/src/utils/async-pageable.ts

This file was deleted.

22 changes: 22 additions & 0 deletions packages/durabletask-js/test/when-all-task.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,26 @@ describe("WhenAllTask", () => {
expect(err.errors[0]).toBe(error0);
expect(err.errors[1]).toBe(error2);
});

// Issue #217: CompositeTask has no separate failed-task counter. `_completedTasks`
// counts every settled child alike, successful and failed, which is why the unused
// `_failedTasks` field was safe to delete.
it("should count both successful and failed children in completedTasks", () => {
const child1 = new CompletableTask<number>();
const child2 = new CompletableTask<number>();
const task = new WhenAllTask([child1, child2]);

child1.complete(1);
expect(task.completedTasks).toBe(1);
expect(task.pendingTasks()).toBe(1);

// The failing child increments the SAME counter as the successful one.
child2.fail("child failed");
expect(task.completedTasks).toBe(2);
expect(task.pendingTasks()).toBe(0);

// Every child is now settled, so wait-all completes the task as failed.
expect(task.isComplete).toBe(true);
expect(task.isFailed).toBe(true);
});
});
21 changes: 21 additions & 0 deletions packages/durabletask-js/test/when-any-task.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,25 @@ describe("WhenAnyTask", () => {
expect(task.isComplete).toBe(false);
expect(task.isFailed).toBe(false);
});

// Issue #217: a failed completion settles WhenAny immediately and is treated the
// same as a successful one — failures are not tracked separately, so a later
// successful child does not override the already-settled failed child.
it("should keep the first failed child as the result even if a later child succeeds", () => {
const child1 = new CompletableTask<number>();
const child2 = new CompletableTask<number>();
const task = new WhenAnyTask([child1, child2]);

child1.fail("first failed");

expect(task.isComplete).toBe(true);
// WhenAny itself is not failed — it simply returns the settled (failed) child.
expect(task.isFailed).toBe(false);
expect(task.getResult()).toBe(child1);

// A subsequent successful completion must not replace the already-settled failed child.
child2.complete(99);
expect(task.getResult()).toBe(child1);
expect(task.getResult().isFailed).toBe(true);
});
});
Loading