Skip to main content

Command Palette

Search for a command to run...

Future Methods

Updated
β€’11 min readβ€’View as Markdown

βš™οΈ Future Methods in Salesforce: A Complete Guide

πŸ” What Are Future Methods?

Future methods in Salesforce are used to execute operations asynchronouslyβ€”in the backgroundβ€”using the @future annotation. This allows you to perform tasks without blocking the user experience, especially useful for:

  • Time-consuming processes

  • Web service callouts

  • Isolating DML operations on different sObject types to avoid Mixed DML exceptions

Each future method is queued and executed when system resources become available, making it a powerful tool for managing background tasks efficiently.


βœ… Benefits of Using Future Methods

  • πŸ”„ Non-blocking execution: Main thread continues without waiting for the method to complete.

  • πŸ“ˆ Higher governor limits: Increased SOQL queries and heap size limits.

  • πŸ” Up to 50 future calls per transaction


πŸ› οΈ How to Define a Future Method

To define a method as a future method:

  • Use the @future annotation

  • The method must be static

  • Return type must be void

πŸ”§ Example:

public class AsyncClass {

    @future
    public static void asyncMethod() {
        // Future code goes here
    }
}

⚠️ Important Considerations

RuleDescription
βœ… Must be staticAll future methods must be declared as static.
βœ… Must return voidNo return values allowed.
βœ… Only primitive parametersAccepts primitive data types, arrays, or collections of primitive types.
❌ Cannot accept sObjectsYou cannot pass standard or custom objects directly.
❌ No chainingOne future method cannot call another future method.
⚠️ Limit of 50A maximum of 50 future calls per transaction is allowed.

πŸ” How to Track Future Method Execution

🧭 Through Setup UI:

  • Navigate to:
    Setup β†’ Environments β†’ Jobs β†’ Apex Jobs

  • Here, you can:

    • View job status

    • Identify method name

    • Check execution time

    • Find Job ID

🧾 Using SOQL Query:

You can also track future job details using this query:

SELECT Id, JobType, ApexClassId, Status, JobItemsProcessed, TotalJobItems, 
       NumberOfErrors, MethodName, CompletedDate, ExtendedStatus, 
       ParentJobId, LastProcessed 
FROM AsyncApexJob 
WHERE Id = '7075h00005vi0zR'

🧠 When to Use Future Methods

Use future methods when:

  • You need to make a callout to an external service

  • You want to execute logic after DML, asynchronously

  • You're trying to avoid Mixed DML exceptions

  • You’re handling background jobs that don't need to block the user

βš™οΈ Understanding System.isFuture() and Mixed DML Exception in Apex

πŸ”Ž What is System.isFuture()?

System.isFuture() is a method in Apex that returns a Boolean value (true or false) to indicate whether the current context is executing within a future method.

βœ… Use Case

You can use System.isFuture() to conditionally execute logic only when code is running in a @future annotated method.

πŸ“˜ Example

SynchronousClass1.apxc

public class SynchronousClass1 {

    public static void syncMethod1() {
        if(System.isFuture()) {
            System.debug('Inside if');
        } else {
            System.debug('Inside else');
        }
    }
}

AsyncClass.apxc

public class AsyncClass {

    @future
    public static void asyncMethod1() {
        System.debug('Inside future method before calling sync');

        SynchronousClass1.syncMethod1();

        System.debug('Inside future method after calling sync');
    }
}

πŸ§ͺ Running the Future Method

Execute the following from anonymous window in the Developer Console:

AsyncClass.asyncMethod1();

πŸ” Output in Logs (Simplified)

Inside future method before calling sync
Inside if
Inside future method after calling sync

This confirms that syncMethod1() detects it is running in a future context.


🚫 What is a Mixed DML Exception?

A Mixed DML Exception occurs when a transaction attempts to perform DML operations on setup and non-setup objects in the same context.

❗ Setup vs Non-Setup Objects

Setup ObjectsNon-Setup Objects
UserOpportunity
UserRoleAccount
PermissionSetCustom Objects
GroupLead, Case, etc.

πŸ’₯ Problem Example

public class SynchronousClass1 {

    public static void syncMethod1() {
        PermissionSet perSet = new PermissionSet();
        perSet.Label = 'Apex Per Set';
        perSet.Name = 'ApexPerSet';
        insert perSet;

        Opportunity opp = new Opportunity();
        opp.Name = 'Apex Opportunity';
        opp.StageName = 'Closed Won';
        opp.CloseDate = System.today();
        insert opp; // πŸ’₯ This causes a Mixed DML Exception
    }
}

πŸ›‘ Error Thrown:

System.DmlException: Insert failed. First exception on row 0; 
first error: MIXED_DML_OPERATION, DML operation on setup object 
is not permitted after you have updated a non-setup object: Opportunity, 
original object: PermissionSet

βœ… How to Resolve Mixed DML Exception?

Use asynchronous execution to separate setup and non-setup DML operations into different transactions.

πŸ› οΈ Solution

SynchronousClass1.apxc

public class SynchronousClass1 {

    public static void syncMethod1() {
        Opportunity opp = new Opportunity();
        opp.Name = 'Apex Opportunity';
        opp.StageName = 'Closed Won';
        opp.CloseDate = System.today();
        insert opp;

        AsyncClass.asyncMethod1(); // Moves setup DML to async context
    }
}

AsyncClass.apxc

public class AsyncClass {

    @future
    public static void asyncMethod1() {
        PermissionSet perSet = new PermissionSet();
        perSet.Label = 'Apex Per Set';
        perSet.Name = 'ApexPerSet';
        insert perSet;
    }
}

This prevents Mixed DML errors by handling DML operations on different object types in separate threads.


πŸ§ͺ How to Handle Mixed DML in Test Classes?

When writing test classes, we can use System.runAs() and @testSetup to separate setup and business data creation.

πŸ§ͺ Example Test Class

@isTest
public class TestClass {

    @testSetup
    public static void setupTestData() {
        // Insert Setup object
        PermissionSet perSet = new PermissionSet();
        perSet.Label = 'Apex Per Set';
        perSet.Name = 'ApexPerSet';
        insert perSet;

        // Run non-setup DML as another user
        User u = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId() LIMIT 1];

        System.runAs(u) {
            Opportunity opp = new Opportunity();
            opp.Name = 'Apex Opportunity';
            opp.StageName = 'Closed Won';
            opp.CloseDate = System.today();
            insert opp;
        }
    }

    @isTest
    public static void myMethod() {
        // Test logic here
    }
}

πŸ“Œ Summary

FeaturePurpose
System.isFuture()Check if logic is running in a future context
Mixed DML ErrorOccurs when setup and non-setup DML are in same transaction
ResolutionMove one part of DML to a @future method to split the context
Test Class HandlingUse @testSetup and System.runAs() to isolate transactions

πŸ”„ Calling a Future Method from a Trigger in Salesforce

In Apex, using future methods inside a trigger is a common approach when you need to perform resource-intensive or asynchronous tasks such as external callouts or long-running operations. In this example, we demonstrate how to delete child Contacts when an Account's isActive__c checkbox is set to false.


🧩 Scenario

Business Requirement:
When an Account is updated and its isActive__c checkbox becomes false, all related child Contacts should be deleted.

Since deleting child records can be a heavy operation and should not block the main trigger execution, we handle the deletion asynchronously using a future method.


πŸ› οΈ Implementation Breakdown


βœ… 1. Trigger Definition

AccountTrigger.apxt

trigger AccountTrigger on Account (after update) {
    if(Trigger.isAfter && Trigger.isUpdate) {
        AccountTriggerHandler.deleteContacts(Trigger.new);
    } 
}
  • This is an after update trigger.

  • It forwards the logic to a handler class to keep the trigger clean and modular.


βœ… 2. Trigger Handler Class

AccountTriggerHandler.apxc

public class AccountTriggerHandler {

    public static void deleteContacts(List<Account> accNewList) {

        Set<Id> accountIds = new Set<Id>();

        for(Account acc : accNewList) {
            if(acc.isActive__c == false) {
                accountIds.add(acc.Id);
            }
        }

        // Call async future method to delete contacts
        AsyncClass.asyncMethod1(accountIds);
    }
}
  • Collects the IDs of Account records where isActive__c == false.

  • Passes these IDs to a @future method for asynchronous contact deletion.


βœ… 3. Future Method Class

AsyncClass.apxc

public class AsyncClass {

    @future
    public static void asyncMethod1(Set<Id> accountIds) {
        delete [SELECT Id FROM Contact WHERE AccountId IN :accountIds];
    }
}
  • Uses the @future annotation to run asynchronously.

  • Deletes all child Contacts where AccountId is in the provided list.

⚠️ Note: @future methods must use only primitive data types (or collections of them) as parameters. Set<Id> is allowed because Id is a primitive.


πŸ“ˆ Advantages of Using Future Method in This Case

BenefitDescription
🧡 Asynchronous ExecutionFrees up the main trigger thread, improving performance
πŸ“› Avoid Mixed DML ErrorsPrevents common DML issues in trigger context
πŸ“Š Better ScalabilityOffloads large deletion tasks outside synchronous limit boundaries

πŸ§ͺ Tips for Testing

When testing this logic in Apex test classes, remember:

  • Call Test.startTest() and Test.stopTest() to ensure the future method runs during test execution.

  • Insert test Account and Contact records first.

  • Update the Account with isActive__c = false to trigger the logic.


πŸ“Œ Summary

ComponentPurpose
AccountTriggerTriggers the logic on account update
AccountTriggerHandlerExtracts IDs of inactive accounts
AsyncClass.asyncMethod1()Deletes child contacts asynchronously via @future

🌐 HTTP Callouts and Status Codes in Salesforce


🧾 What Are HTTP Status Codes?

When a browser requests information from a web server, the server responds with an HTTP status codeβ€”a three-digit number indicating the result of the request.

πŸ“š Categories of HTTP Status Codes:

Code RangeTypeDescription
1xxInformationalRequest received, continuing process
2xxSuccessRequest successfully received and processed
3xxRedirectionFurther action needs to be taken
4xxClient ErrorProblem with the request
5xxServer ErrorProblem with the server

πŸ“˜ Example: A 301 redirect tells the browser (and search engines) that a page has permanently moved.

πŸ”— Public API for testing :
https://gorest.co.in/public/v2/posts


βš™οΈ What Is @future(callout=true)?

To perform asynchronous web service callouts, you use the @future annotation with callout=true.

πŸ”’ Without this, you'll get:

System.CalloutException: Callout not allowed from this future method.

βœ… Correct usage:

@future(callout=true)
public static void callExternalWebservice() {
    // perform callout here
}

🌍 What Are Remote Site Settings in Salesforce?

Before making an external callout from Salesforce (via Apex, Visualforce, or JavaScript remoting), you must whitelist the external URL using Remote Site Settings.

βž• Steps to Add a Remote Site:

  1. Go to Setup β†’ Search "Remote Site Settings"

  2. Click New Remote Site

  3. Enter:

    • Remote Site Name

    • Remote Site URL (e.g., https://gorest.co.in)

    • Optional description

  4. Click Save

⚠️ If not added, you'll get this error:

System.CalloutException: Unauthorized endpoint.

πŸ” Making a Callout Using @future(callout=true)

βœ… Apex Class

public class FutureCallout {

    @future(callout=true)
    public static void callExternalWebservice() {

        Http h = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://gorest.co.in/public/v2/posts');
        req.setMethod('GET');

        HttpResponse res = h.send(req);

        System.debug('Status Code : ' + res.getStatusCode());
        System.debug('Body : ' + res.getBody());
    }
}

πŸ§ͺ Writing a Test Class for Callouts

Since real callouts are not allowed in test methods, Salesforce provides the HttpCalloutMock interface.


βœ… Mock Class: CalloutMock

@isTest
public class CalloutMock implements HttpCalloutMock {

    public HttpResponse respond(HttpRequest req) {
        HttpResponse res = new HttpResponse();
        res.setStatusCode(200);
        res.setBody('Test JSON Body');
        return res;
    }
}

βœ… Test Class: FutureCallout_Test

@isTest
public class FutureCallout_Test {

    @isTest
    public static void callExternalWebservice() {
        Test.startTest();
        Test.setMock(HttpCalloutMock.class, new CalloutMock());
        FutureCallout.callExternalWebservice();
        Test.stopTest();
    }
}

🎯 Summary

ConceptDescription
@future(callout=true)Enables asynchronous HTTP callouts
Remote Site SettingsRequired to authorize external domains
HttpCalloutMockInterface to simulate callouts in test classes
HTTP Status CodesInform response success or failure (e.g., 200 OK, 404 Not Found)

⚑ @future Annotation in Apex: Pros & Cons


βœ… Advantages of Using @future Methods

AdvantageDescription
πŸ“ž Enables Callouts in TriggersCallouts are not directly allowed from triggers. Using @future(callout=true) allows asynchronous callouts from triggers.
⏱ Helps Avoid CPU Time Limit & SOQL 101 ErrorsPushes non-priority or heavy logic out of the synchronous context, reducing the risk of governor limit breaches.
πŸ”„ Increases Timeout FlexibilityFuture methods allow longer execution time, which helps with operations like long-running API calls.

❌ Drawbacks / Limitations of @future Methods

LimitationDescription
πŸ” No Nested Future CallsA future method cannot call another future method. Nesting is not allowed.
🚫 Incompatible with Batch ApexFuture methods cannot be invoked from Batch Apex classes.
πŸ›‘ No Return ValuesFuture methods must have a void return type β€” they cannot return data back to the caller.
πŸ“¦ Only Primitive ParametersYou can only pass primitive data types, arrays, or collections of primitive types β€” not sObjects or custom types.
πŸ”’ Limited to 50 Calls per TransactionMaximum of 50 @future method calls allowed per Apex transaction. Exceeding this limit throws an exception.

πŸ“ Summary Table

CriteriaSupported in @future?
Calloutsβœ… (use @future(callout=true))
Return Values❌ Only void allowed
sObject or Complex Parameter Types❌ Not supported
Invocation in Batch Apex❌ Not supported
Nested Future Calls❌ Not allowed
Max Calls per Transaction❌ Limited to 50

More from this blog

B

BlackBUC

29 posts