Skip to content

Commit 81cbae2

Browse files
committed
Fix stack overflow with many aborted tasks
Fixes #217
1 parent 27839dc commit 81cbae2

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

source/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,10 @@ export default class PQueue<QueueType extends Queue<RunFunction, EnqueueOptionsT
354354
reject(error);
355355
this.emit('error', error);
356356
} finally {
357-
this.#next();
357+
// Use queueMicrotask to prevent deep recursion while maintaining timing
358+
queueMicrotask(() => {
359+
this.#next();
360+
});
358361
}
359362
}, options);
360363

test/test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,3 +1654,30 @@ test('intervalCap should be respected with high concurrency (issue #126)', async
16541654
// Check that tasks actually completed (basic sanity check)
16551655
t.is(results.length, 5000, 'All tasks should complete');
16561656
});
1657+
1658+
test('should not cause stack overflow with many aborted tasks', async t => {
1659+
const queue = new PQueue({concurrency: 1});
1660+
const controller = new AbortController();
1661+
1662+
// Add many tasks exactly like in issue #217
1663+
const taskCount = 10_000;
1664+
const promises: Array<Promise<any>> = [];
1665+
1666+
for (let index = 0; index < taskCount; index++) {
1667+
const promise = queue.add(() => 1 + 1, {signal: controller.signal}).catch(() => {
1668+
// Expected abort error
1669+
});
1670+
1671+
promises.push(promise);
1672+
}
1673+
1674+
// Abort all tasks immediately
1675+
controller.abort();
1676+
1677+
// This should not cause a stack overflow
1678+
await Promise.all(promises);
1679+
1680+
// Verify queue state
1681+
t.is(queue.pending, 0);
1682+
t.is(queue.size, 0);
1683+
});

0 commit comments

Comments
 (0)