Future Methods
βοΈ 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
@futureannotationThe method must be
staticReturn type must be
void
π§ Example:
public class AsyncClass {
@future
public static void asyncMethod() {
// Future code goes here
}
}
β οΈ Important Considerations
| Rule | Description |
β
Must be static | All future methods must be declared as static. |
β
Must return void | No return values allowed. |
| β Only primitive parameters | Accepts primitive data types, arrays, or collections of primitive types. |
| β Cannot accept sObjects | You cannot pass standard or custom objects directly. |
| β No chaining | One future method cannot call another future method. |
| β οΈ Limit of 50 | A maximum of 50 future calls per transaction is allowed. |
π How to Track Future Method Execution
π§ Through Setup UI:
Navigate to:
Setup β Environments β Jobs β Apex JobsHere, 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 Objects | Non-Setup Objects |
| User | Opportunity |
| UserRole | Account |
| PermissionSet | Custom Objects |
| Group | Lead, 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
| Feature | Purpose |
System.isFuture() | Check if logic is running in a future context |
| Mixed DML Error | Occurs when setup and non-setup DML are in same transaction |
| Resolution | Move one part of DML to a @future method to split the context |
| Test Class Handling | Use @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
Accountrecords whereisActive__c == false.Passes these IDs to a
@futuremethod 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
@futureannotation to run asynchronously.Deletes all child
ContactswhereAccountIdis in the provided list.
β οΈ Note:
@futuremethods must use only primitive data types (or collections of them) as parameters.Set<Id>is allowed becauseIdis a primitive.
π Advantages of Using Future Method in This Case
| Benefit | Description |
| π§΅ Asynchronous Execution | Frees up the main trigger thread, improving performance |
| π Avoid Mixed DML Errors | Prevents common DML issues in trigger context |
| π Better Scalability | Offloads large deletion tasks outside synchronous limit boundaries |
π§ͺ Tips for Testing
When testing this logic in Apex test classes, remember:
Call
Test.startTest()andTest.stopTest()to ensure the future method runs during test execution.Insert test
AccountandContactrecords first.Update the
AccountwithisActive__c = falseto trigger the logic.
π Summary
| Component | Purpose |
AccountTrigger | Triggers the logic on account update |
AccountTriggerHandler | Extracts 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 Range | Type | Description |
| 1xx | Informational | Request received, continuing process |
| 2xx | Success | Request successfully received and processed |
| 3xx | Redirection | Further action needs to be taken |
| 4xx | Client Error | Problem with the request |
| 5xx | Server Error | Problem 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:
Go to Setup β Search "Remote Site Settings"
Click New Remote Site
Enter:
Remote Site Name
Remote Site URL (e.g.,
https://gorest.co.in)Optional description
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
| Concept | Description |
@future(callout=true) | Enables asynchronous HTTP callouts |
| Remote Site Settings | Required to authorize external domains |
HttpCalloutMock | Interface to simulate callouts in test classes |
| HTTP Status Codes | Inform response success or failure (e.g., 200 OK, 404 Not Found) |
β‘ @future Annotation in Apex: Pros & Cons
β
Advantages of Using @future Methods
| Advantage | Description |
| π Enables Callouts in Triggers | Callouts are not directly allowed from triggers. Using @future(callout=true) allows asynchronous callouts from triggers. |
| β± Helps Avoid CPU Time Limit & SOQL 101 Errors | Pushes non-priority or heavy logic out of the synchronous context, reducing the risk of governor limit breaches. |
| π Increases Timeout Flexibility | Future methods allow longer execution time, which helps with operations like long-running API calls. |
β Drawbacks / Limitations of @future Methods
| Limitation | Description |
| π No Nested Future Calls | A future method cannot call another future method. Nesting is not allowed. |
| π« Incompatible with Batch Apex | Future methods cannot be invoked from Batch Apex classes. |
| π No Return Values | Future methods must have a void return type β they cannot return data back to the caller. |
| π¦ Only Primitive Parameters | You can only pass primitive data types, arrays, or collections of primitive types β not sObjects or custom types. |
| π’ Limited to 50 Calls per Transaction | Maximum of 50 @future method calls allowed per Apex transaction. Exceeding this limit throws an exception. |
π Summary Table
| Criteria | Supported 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 |

