Open
Conversation
f8af6ee to
7ba019c
Compare
Add Async.queueable(List<QueueableJob>) factory method and QueueableBuilder List constructor to enqueue multiple jobs as a single chain in one call. Add QueueableChainBuilder class with fluent add()/enqueue()/chain() API for incremental chain composition via Async.chainBuilder(). Make ERROR_MESSAGE_NO_JOB_SET public for cross-class access. Includes 9 new test methods covering happy paths, edge cases, and mixed usage.
7ba019c to
3854d44
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add Async.queueable(List) factory method and QueueableBuilder List constructor to enqueue multiple jobs as a single chain in one call.
Add QueueableChainBuilder class with fluent add()/enqueue()/chain() API for incremental chain composition via Async.chainBuilder().
Includes 9 new test methods covering happy paths, edge cases, and mixed usage.
Description
Type of Change
Changes Made
Motivation
When enqueuing many queueable jobs in a loop, the current API requires manual chain bookkeeping:
This is verbose, error-prone (off-by-one on the last job, forgetting to call
enqueue()separately), and doesn't feel like the rest of the fluent API.Why this matters for high job counts
The motivation section above focuses on code ergonomics, but there's a more critical reason: the current loop pattern hits governor limits.
When you enqueue jobs individually in a loop:
Each
.enqueue()call creates a separate chain. After the 50thSystem.enqueueJob()(Salesforce limit per synchronous transaction), every subsequent call overflows intoQueueableChain.executeOrReplaceInitialQueueableChainSchedulableJob(), which callsSystem.abortJob()+System.schedule()— 2 DML statements per overflow job:System.enqueueJob()— each creates its own chainSystem.schedule()(first overflow, no abort)System.abortJob()+System.schedule()At 101 jobs you hit
System.LimitException: Too many DML statements: 151.How the new APIs solve this
Async.queueable(list).enqueue()andAsync.chainBuilder()...enqueue()put all jobs into a single chain with oneSystem.enqueueJob()call. The overflow mechanism fires at most once (1System.schedule()), so DML stays at ~2 total regardless of job count — making 500+ jobs safe.What this PR adds
Two new entry points that stay consistent with the existing
Async.*design language:1.
Async.queueable(List<QueueableJob>)— when you already have a listAll jobs are automatically chained in order. The last job becomes the active builder target, so you can still apply configuration:
Validates input —
nullor empty list throwsIllegalArgumentException.2.
Async.chainBuilder()— when you're composing incrementallySupports adding jobs one at a time or in batches:
Also supports
.chain()instead of.enqueue()for nesting inside an outer chain.Changes
Async.clsqueueable(List<QueueableJob>)andchainBuilder()factory methodsQueueableBuilder.clsQueueableBuilder(List<QueueableJob>)constructor — chains N-1 jobs, sets last as activeQueueableChainBuilder.clsadd(job),add(list),enqueue(),chain()AsyncTest.clsTest coverage
shouldEnqueueListOfQueueablesSuccessfully— 5 jobs via listshouldEnqueueListOfQueueablesWithDelaySuccessfully— list +.delay(1)shouldFailOnEmptyListOfQueueables— empty list → exceptionshouldFailOnNullListOfQueueables— null → exceptionshouldEnqueueChainBuilderSuccessfully— 3 jobs via chainBuildershouldChainWithChainBuilderWithoutEnqueue—.chain()instead of.enqueue()shouldEnqueueChainBuilderWithTwoJobs— verifies chain state job countshouldEnqueueChainBuilderWithListOfJobs—add(list)with 4 jobsshouldEnqueueChainBuilderWithListAndSingleJobs— mixedadd(single)+add(list)= 5 jobsRelated Issues
Fixes #
Closes #
Testing
npm test)npm run lint)npm run prettier:verify)Screenshots
Checklist
Additional Notes