From 014f2a175410d6214a774c961b10e1f5f07c514a Mon Sep 17 00:00:00 2001 From: luojiyin Date: Tue, 1 Sep 2026 16:51:45 +0800 Subject: [PATCH] fix(concurrency): stop scheduling after failure --- __tests__/run-tasks-with-limit.spec.ts | 42 ++++++++++++++++++++++++++ src/utils/run-tasks-with-limit.ts | 11 +++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/__tests__/run-tasks-with-limit.spec.ts b/__tests__/run-tasks-with-limit.spec.ts index 7a5cf75..dbbc957 100644 --- a/__tests__/run-tasks-with-limit.spec.ts +++ b/__tests__/run-tasks-with-limit.spec.ts @@ -40,4 +40,46 @@ describe("runTasksWithLimit", () => { runTasksWithLimit([async () => 1, async () => Promise.reject(failure)], 2) ).rejects.toBe(failure); }); + + test("stops scheduling new tasks after the first rejection", async () => { + const failure = new Error("task failed"); + const started: number[] = []; + let markSecondStarted!: () => void; + let releaseSecond!: () => void; + const secondStarted = new Promise((resolve) => { + markSecondStarted = resolve; + }); + const secondRelease = new Promise((resolve) => { + releaseSecond = resolve; + }); + const tasks = [ + async () => { + started.push(0); + await secondStarted; + throw failure; + }, + async () => { + started.push(1); + markSecondStarted(); + await secondRelease; + return 1; + }, + async () => { + started.push(2); + return 2; + }, + async () => { + started.push(3); + return 3; + }, + ]; + + const run = runTasksWithLimit(tasks, 2); + + await expect(run).rejects.toBe(failure); + releaseSecond(); + await new Promise((resolve) => setImmediate(resolve)); + + expect(started).toEqual([0, 1]); + }); }); diff --git a/src/utils/run-tasks-with-limit.ts b/src/utils/run-tasks-with-limit.ts index 9659ba8..f3c0e76 100644 --- a/src/utils/run-tasks-with-limit.ts +++ b/src/utils/run-tasks-with-limit.ts @@ -4,11 +4,18 @@ export async function runTasksWithLimit( ): Promise { const results: T[] = []; let index = 0; + let failed = false; async function runNext(): Promise { - while (index < tasks.length) { + while (!failed && index < tasks.length) { const currentIndex = index++; - results[currentIndex] = await tasks[currentIndex](); + + try { + results[currentIndex] = await tasks[currentIndex](); + } catch (error) { + failed = true; + throw error; + } } }