Salesforce Async Process – Make the right choice

Introduction

In the Salesforce ecosystem, asynchronous processes are essential tools that allow developers to execute operations in the background, helping overcome platform limitations while creating more responsive user experiences

Why Use Asynchronous Processing?

Before diving into the specifics, let’s understand why asynchronous processing is crucial in Salesforce:

  • Governor Limit Management: Overcome CPU time limits, heap size constraints, and DML operation restrictions
  • Enhanced User Experience: Prevent UI freezing during complex operations
  • Background Processing: Handle resource-intensive tasks without affecting user interactions
  • Integration Flexibility: Make external callouts without timeout concerns
  • Scheduled Execution: Run processes at specific times without manual intervention

1. Future Methods

Future methods are the simplest form of asynchronous processing in Salesforce, allowing code execution in a separate thread at a later time.

Use Cases

  • Making callouts to external web services
  • Performing CPU-intensive calculations
  • Separating operations that don’t need immediate results

Example Code

public class AccountProcessor {
    @future
    public static void updateAccountRevenue(Set<Id> accountIds) {
        List<Account> accountsToUpdate = new List<Account>();
        
        for(Account acc : [SELECT Id, AnnualRevenue FROM Account WHERE Id IN :accountIds]) {
            // Perform complex calculation
            Double newRevenue = calculateNewRevenue(acc);
            acc.AnnualRevenue = newRevenue;
            accountsToUpdate.add(acc);
        }
        
        if(!accountsToUpdate.isEmpty()) {
            update accountsToUpdate;
        }
    }
    
    private static Double calculateNewRevenue(Account acc) {
        // Complex calculation simulation
        return acc.AnnualRevenue != null ? acc.AnnualRevenue * 1.1 : 0;
    }
}

// Calling the future method
Set<Id> accountIds = new Set<Id>{'001XXXXXXXXXXXXXXX', '001YYYYYYYYYYYYYYY'};
AccountProcessor.updateAccountRevenue(accountIds);

Pros

  • Simple to implement with just an @future annotation
  • Runs with system-level permissions
  • Perfect for one-off async operations

Cons

  • Limited to primitive data types as parameters (no Salesforce objects)
  • Cannot chain future methods
  • Limited to 50 future calls per transaction
  • Cannot track the status of execution
  • No control over the execution order

2. Batch Apex

Batch Apex allows processing of large record volumes by breaking data into manageable chunks, making it ideal for bulk operations.

Use Cases

  • Mass data updates or calculations
  • Data cleansing or normalization
  • Scheduled data migrations
  • Processing records that exceed governor limits

Example Code

public class AccountUpdateBatch implements Database.Batchable<sObject> {
    public Database.QueryLocator start(Database.BatchableContext BC) {
        // Get all accounts to process
        return Database.getQueryLocator('SELECT Id, Name, Description, LastModifiedDate FROM Account WHERE LastModifiedDate = LAST_N_DAYS:30');
    }
    
    public void execute(Database.BatchableContext BC, List<Account> scope) {
        List<Account> accountsToUpdate = new List<Account>();
        
        for(Account acc : scope) {
            acc.Description = 'Updated by batch on ' + System.today();
            accountsToUpdate.add(acc);
        }
        
        if(!accountsToUpdate.isEmpty()) {
            update accountsToUpdate;
        }
    }
    
    public void finish(Database.BatchableContext BC) {
        // Send an email notification when the batch is complete
        Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
        String[] toAddresses = new String[] {'[email protected]'};
        mail.setToAddresses(toAddresses);
        mail.setSubject('Account Update Batch Completed');
        mail.setPlainTextBody('The batch job to update Account descriptions has completed.');
        Messaging.sendEmail(new Messaging.SingleEmailMessage[] { mail });
    }
}

// Execute the batch job
Id batchId = Database.executeBatch(new AccountUpdateBatch(), 200);

Pros

  • Can process millions of records
  • Configurable batch size (1-2000 records)
  • Automatic chunking and governor limit reset for each chunk
  • Built-in retry mechanism for failed batches
  • Detailed status tracking through Apex Jobs UI
  • Has finish method for completion actions

Cons

  • More complex implementation requiring three methods
  • Not ideal for real-time operations
  • Limited to 5 concurrent batch jobs
  • Higher resource consumption
  • Cannot chain batch jobs easily

3. Queueable Apex

Queueable Apex combines the flexibility of future methods with the power of batch processing, offering a more modern way to handle asynchronous processing.

Use Cases

  • Complex asynchronous operations requiring non-primitive parameters
  • Operations needing chained execution
  • Processes requiring status tracking
  • Jobs that need to be monitored

Example Code

public class AccountContactCreator implements Queueable {
    private List<Account> accounts;
    
    public AccountContactCreator(List<Account> accounts) {
        this.accounts = accounts;
    }
    
    public void execute(QueueableContext context) {
        List<Contact> contactsToCreate = new List<Contact>();
        
        for(Account acc : accounts) {
            // Create a contact for each account
            Contact con = new Contact(
                AccountId = acc.Id,
                LastName = acc.Name + ' Contact',
                Email = 'contact@' + acc.Name.toLowerCase().replaceAll(' ', '') + '.com'
            );
            contactsToCreate.add(con);
        }
        
        if(!contactsToCreate.isEmpty()) {
            insert contactsToCreate;
            
            // Chain another queueable job
            if(!Test.isRunningTest()) {
                System.enqueueJob(new AccountOpportunityCreator(accounts));
            }
        }
    }
}

// Class for chained execution
public class AccountOpportunityCreator implements Queueable {
    private List<Account> accounts;
    
    public AccountOpportunityCreator(List<Account> accounts) {
        this.accounts = accounts;
    }
    
    public void execute(QueueableContext context) {
        List<Opportunity> oppsToCreate = new List<Opportunity>();
        
        for(Account acc : accounts) {
            Opportunity opp = new Opportunity(
                AccountId = acc.Id,
                Name = acc.Name + ' Opportunity',
                StageName = 'Prospecting',
                CloseDate = System.today().addDays(30)
            );
            oppsToCreate.add(opp);
        }
        
        if(!oppsToCreate.isEmpty()) {
            insert oppsToCreate;
        }
    }
}

// Execute the queueable job
List<Account> newAccounts = [SELECT Id, Name FROM Account WHERE CreatedDate = TODAY];
Id jobId = System.enqueueJob(new AccountContactCreator(newAccounts));

Pros

  • Accepts complex data types as parameters
  • Supports job chaining
  • Job status tracking via Apex Jobs UI
  • More modern and flexible than future methods
  • Higher limits (50 jobs can be queued per transaction)

Cons

  • Only one job can be chained from another
  • Limited to 50 queueable jobs per transaction
  • Cannot be used in certain contexts like batch finish methods

4. Scheduled Apex

Scheduled Apex enables running code at specified times using cron expressions, allowing for automation of recurring tasks.

Use Cases

  • Nightly data cleanups or reconciliations
  • Periodic report generation
  • Recurring external system synchronizations
  • Time-based business processes

Example Code

public class DailyAccountSummary implements Schedulable {
    public void execute(SchedulableContext ctx) {
        // Count accounts created today
        Integer newAccounts = [SELECT COUNT() FROM Account WHERE CreatedDate = TODAY];
        
        // Count opportunities closed today
        Integer closedOpps = [SELECT COUNT() FROM Opportunity WHERE CloseDate = TODAY AND IsClosed = true];
        
        // Create a summary record
        DailySummary__c summary = new DailySummary__c(
            Date__c = System.today(),
            NewAccounts__c = newAccounts,
            ClosedOpportunities__c = closedOpps
        );
        
        insert summary;
        
        // You could also call a batch job from here
        Database.executeBatch(new AccountUpdateBatch());
    }
}

// Schedule the job to run daily at 11:00 PM
String jobName = 'Daily Account Summary';
String cronExp = '0 0 23 * * ?';
System.schedule(jobName, cronExp, new DailyAccountSummary());

Pros

  • Runs at specific times without manual intervention
  • Uses standard cron expressions for scheduling
  • Can be scheduled via Apex or Setup UI
  • Perfect for recurring automated processes
  • Can kick off other async processes (batch, queueable)

Cons

  • Limited to 100 scheduled jobs at organization level
  • Lacks granular scheduling options
  • No built-in error recovery
  • Cannot use certain operations like callouts directly (need to chain to queueable or future)

5. Platform Events

Platform Events offer a publish-subscribe model for real-time event-driven architecture, allowing systems to communicate efficiently.

Use Cases

  • Real-time notifications
  • System integrations
  • Decoupled architecture implementations
  • Business event broadcasting
  • IoT device communication

Example Code

// Define a Platform Event (configuration in Setup)
// OrderEvent__e with fields: Order_Number__c, Status__c, Amount__c

// Publishing a Platform Event
public class OrderProcessor {
    public static void publishOrderUpdate(String orderNumber, String status, Decimal amount) {
        // Create event instance
        OrderEvent__e orderEvent = new OrderEvent__e(
            Order_Number__c = orderNumber,
            Status__c = status,
            Amount__c = amount
        );
        
        // Publish event
        Database.SaveResult result = EventBus.publish(orderEvent);
        
        // Verify publication
        if (!result.isSuccess()) {
            for(Database.Error error : result.getErrors()) {
                System.debug('Error publishing event: ' + error.getStatusCode() + ' - ' + error.getMessage());
            }
        }
    }
}

// Trigger to subscribe to the event
trigger OrderEventTrigger on OrderEvent__e (after insert) {
    List<Order_Notification__c> notifications = new List<Order_Notification__c>();
    
    for (OrderEvent__e event : Trigger.new) {
        // Create a record to store the notification
        Order_Notification__c notification = new Order_Notification__c(
            Order_Number__c = event.Order_Number__c,
            Status__c = event.Status__c,
            Amount__c = event.Amount__c,
            Notification_Date__c = System.now()
        );
        notifications.add(notification);
    }
    
    if (!notifications.isEmpty()) {
        insert notifications;
    }
}

Pros

  • Real-time communication between systems
  • Decoupled architecture (publishers don’t wait for subscribers)
  • No governor limit impacts across the event boundary
  • High volume capacity
  • Can be consumed by external systems via CometD

Cons

  • Requires additional setup and configuration
  • More complex implementation
  • Potential for missed events if no subscribers are active
  • Limited retention period
  • May require volume monitoring for high-throughput systems

6. Change Data Capture (CDC)

Change Data Capture provides a way to track and respond to data changes in Salesforce records, enabling real-time integrations.

Use Cases

  • Real-time data synchronization with external systems
  • Audit trail creation
  • Cache invalidation
  • Data replication
  • Event-driven integrations

Example Code

// Configure CDC in Setup for Account entity

// Apex trigger to handle CDC events
trigger AccountChangeTrigger on AccountChangeEvent (after insert) {
    List<CDC_Log__c> logs = new List<CDC_Log__c>();
    
    for (AccountChangeEvent event : Trigger.new) {
        // Get all changed fields
        EventBus.ChangeEventHeader header = event.ChangeEventHeader;
        List<String> changedFields = header.getChangedFields();
        
        // Record the change
        CDC_Log__c log = new CDC_Log__c(
            Record_ID__c = header.recordIds[0],
            Change_Type__c = header.changeType,
            Changed_Fields__c = String.join(changedFields, ', '),
            Change_Time__c = System.now()
        );
        
        // If specific fields changed, take action
        if (changedFields.contains('Industry')) {
            // Get the new value
            String newIndustry = event.Industry;
            log.Field_Value__c = newIndustry;
            
            // Additional processing could go here
        }
        
        logs.add(log);
    }
    
    if (!logs.isEmpty()) {
        insert logs;
    }
}

Pros

  • Built-in capture of record changes
  • Includes field-level change information
  • Includes both old and new values
  • Can be filtered to specific fields
  • Works with standard and custom objects
  • External systems can subscribe via CometD

Cons

  • Higher org-wide consumption of resources
  • Potential performance impact for high-volume objects
  • Requires careful planning for which objects to track
  • Limited retention period
  • More complex setup than other async options

Making the Right Choice: Comparison Table

FeatureFuture MethodBatch ApexQueueable ApexScheduled ApexPlatform EventsCDC
ComplexityLowHighMediumMediumHighHigh
Record VolumeLowVery HighMediumN/AHighHigh
Status TrackingNoYesYesYesPartialPartial
ChainableNoNoYesN/AN/AN/A
Real-timeNoNoNoNoYesYes
External System IntegrationNoNoNoNoYesYes
Complex ParametersNoYesYesYesYesN/A
Granular SchedulingNoNoNoYesNoNo
Error HandlingPoorGoodGoodBasicGoodGood

Conclusion

Asynchronous processing in Salesforce provides powerful solutions to overcome platform limitations and build scalable applications. Each method has its own strengths and appropriate use cases:

  • Future Methods: Simple, one-off async operations
  • Batch Apex: High-volume data processing
  • Queueable Apex: Complex, chainable operations
  • Scheduled Apex: Time-based processes
  • Platform Events: Real-time, event-driven architecture
  • Change Data Capture: Data synchronisation and reactive systems

Resources

Amit Singh
Amit Singh

Amit Singh aka @sfdcpanther/pantherschools, a Salesforce Technical Architect, Consultant with over 8+ years of experience in Salesforce technology. 21x Certified. Blogger, Speaker, and Instructor. DevSecOps Champion

Articles: 299

Newsletter Updates

Enter your email address below and subscribe to our newsletter

One comment

Leave a Reply

Your email address will not be published. Required fields are marked *

Discover more from Panther Schools

Subscribe now to keep reading and get access to the full archive.

Continue reading