Skip to content

Getting Started

Async Lib is a powerful Salesforce Apex framework that provides an elegant solution for managing asynchronous processes. It eliminates common limitations like "Too many queueable jobs" errors and offers a unified API for queueable, batchable, and schedulable jobs.

Why Async Lib?

Salesforce Limits

Apex ContextQueueableFutureBatchSchedule
Synchronous or Scheduled* process5050100 in Holding status
5 in Queued or Active status
100
Queueable job150
@future method call10
Batch job10As above in finish() batch method. For start() and execute() methods, the limit is 0.

Key Benefits

  • 🚀 Eliminates Queueable Limits: Automatically handles "Too many queueable jobs" by intelligent chaining and batch overflow
  • 🎯 Unified API: Single, consistent interface for all async job types (Queueable, Batchable, Schedulable)
  • ⚡ Smart Prioritization: Jobs execute based on priority with automatic sorting
  • 🛡️ Advanced Error Handling: Built-in error recovery, rollback options, and continuation strategies
  • 📊 Job Tracking: Comprehensive tracking with custom job IDs and result records
  • ⚙️ Configuration-Driven: Control job behavior through custom metadata without code changes
  • 🔗 Support Finalizers: Execute cleanup logic after job completion with full context

Core Concepts

1. QueueableJob Base Class

All queueable jobs extend the QueueableJob abstract class:

apex
public class MyQueueableJob extends QueueableJob {
  public override void work() {
    // Your business logic here
    System.debug(
      'Processing job: ' + Async.getQueueableJobContext().currentJob.customJobId
    );
  }
}

2. Builder Pattern API

All job types use a fluent builder pattern:

apex
// Queueable Job
Async.queueable(new MyQueueableJob())
    .priority(10)
    .delay(5)
    .enqueue();

// Batch Job
Async.batchable(new MyBatchJob())
    .scopeSize(100)
    .execute();

// Schedulable Job
Async.schedulable(new MySchedulableJob())
    .name('Daily Cleanup')
    .cronExpression('0 0 2 * * ? *')
    .skipWhenAlreadyScheduled()
    .schedule();

3. Automatic Job Chaining

When queueable limits are reached, Async Lib automatically switches to scheduled-batch-based execution, ensuring your jobs always run without hitting Queueable platform limits.

Your First Queueable Job

Let's create a simple job that processes accounts:

Step 1: Create Your Job Class

apex
public class AccountProcessorJob extends QueueableJob {
  private List<Id> accountIds;

  public AccountProcessorJob(List<Id> accountIds) {
    this.accountIds = accountIds;
  }

  public override void work() {
    // Get job context
    Async.QueueableJobContext ctx = Async.getQueueableJobContext();
    System.debug('Processing job: ' + ctx.currentJob.customJobId);

    // Process accounts
    List<Account> accounts = [
      SELECT Id, Name
      FROM Account
      WHERE Id IN :accountIds
    ];
    for (Account acc : accounts) {
      acc.Description = 'Processed by ' + ctx.currentJob.className;
    }
    update accounts;

    System.debug('Processed ' + accounts.size() + ' accounts');
  }
}

Step 2: Enqueue the Job

apex
// Get some account IDs
List<Id> accountIds = new List<Id>{
    '0013000000abcdef',
    '0013000000ghijkl'
};

// Enqueue the job
Async.Result result = Async.queueable(new AccountProcessorJob(accountIds))
    .priority(5)
    .enqueue();

System.debug('Job enqueued with ID: ' + result.customJobId);

Error Handling

Async Lib provides sophisticated error handling options:

apex
Async.queueable(new MyJob())
    .continueOnJobEnqueueFail()    // Don't fail if enqueue fails
    .continueOnJobExecuteFail()    // Continue processing other jobs if this fails
    .rollbackOnJobExecuteFail()    // Rollback any DML if job fails
    .enqueue();

Using Finalizers

Finalizers run after job completion and have access to success/failure context:

apex
public class MyJobFinalizer extends QueueableJob.Finalizer {
    public override void work() {
        Async.QueueableJobContext ctx = Async.getQueueableJobContext();
        FinalizerContext finalizerCtx = ctx.finalizerCtx;

        if (finalizerCtx.getResult() == ParentJobResult.SUCCESS) {
            System.debug('Job completed successfully!');
        } else {
            System.debug('Job failed: ' + finalizerCtx.getException().getMessage());
        }
    }
}

// Attach finalizer within a job
public class MyMainJob extends QueueableJob {
    public override void work() {
        // Do main work...

        // Attach finalizer
        Async.queueable(new MyJobFinalizer())
            .attachFinalizer();
    }
}

Configuration

Control job behavior using Custom Metadata (QueueableJobSetting__mdt):

  1. Go to Setup → Custom Metadata Types → QueueableJobSetting → Manage Records
  2. Create or edit settings:
    • All: Global settings for all jobs
    • Specific Class Name: Settings for specific job classes

Available settings:

  • QueueableJobName__c: The name of the QueueableJob.
  • IsDisabled__c: Disable job execution
  • CreateResult__c: Create AsyncResult__c records for tracking

Async Result Records

When enabled in QueueableJobSetting__mdt (CreateResult__c = true), Async Lib automatically creates an AsyncResult__c record for every job in a chain — including jobs that were skipped — so you can see exactly what happened, and why. Query by ChainId__c to see a whole chain run as a timeline.

Key Fields

  • ChainId__c: Correlation id shared by every job in the same chain run
  • Status__c: Lifecycle outcome — COMPLETED, FAILED, SKIPPED_DEPENDENCY, SKIPPED_CHAIN_STOPPED, SKIPPED_EXPLICIT, SKIPPED_DISABLED
  • CustomJobId__c: Unique job ID generated by Async Lib
  • SalesforceJobId__c: Underlying Salesforce job ID (Queueable, Batch, or Scheduled)
  • ClassName__c: Apex class name of the job
  • Result__c: Platform job result for jobs that ran (SUCCESS, UNHANDLED_EXCEPTION)
  • ExceptionType__c / ExceptionMessage__c: Failure detail when Status__c is FAILED
  • SkipReason__c: Why a skipped job was skipped (e.g. "Dependency 'extract' required SUCCESS but was FAILURE")
  • DependsOnResult__c: Self-lookup to the result of the dependency that decided this job's fate, with RequiredOutcome__c / ActualOutcome__c
  • RetryAttempts__c: How many retries the job went through
  • RetryHistory__c: Per-attempt retry log

These records are never deleted automatically. See AsyncResult Cleanup for the scheduled cleanup batch with separate retention for failed results and the rest.

What's Next?

Now that you understand the basics:

  1. Standard Apex vs Async Lib - See each plain-Apex async pattern next to its Async Lib equivalent.
  2. Explore the API - Learn about all available methods and options:
    1. Queueable API - Detailed information on using Queueable jobs
    2. Batchable API - Detailed information on using Batchable jobs
    3. Schedulable API - Detailed information on using Schedulable jobs
  3. Read the Blog Post - Check out the detailed explanation: Apex Queueable Processing Framework
  4. Initial Queueable Chain Schedulable Explanation - Learn why this job is important for framework to function properly.

Quick Tips

  • Job Naming: Jobs get unique names with timestamps: MyJob::2024-01-15T10:30:45.123Z::1
  • Custom Job IDs: Every job gets a UUID for tracking independent of Salesforce Job IDs
  • Priority Matters: Lower numbers = higher priority. Finalizers always run first.
  • Test Friendly: Framework handles test context automatically
  • Callouts Supported: Use QueueableJob.AllowsCallouts for HTTP callouts