Interview Questions - Future Method in Salesforce - Part 1
Harshit Gupta
10x Salesforce Certified Professional | Actively looking for full time role as a Salesforce Developer | MS in Computer Science | California State University, Long Beach
What are future methods in Salesforce, and why are they used?
Future methods in Salesforce are used to run processes asynchronously. They are typically employed for operations that require higher governor limits or need to be executed in the background to avoid blocking the main transaction. Here's a breakdown of their purpose and usage:
Key Characteristics of Future Methods:
Example of a Future Method:
public class MyClass {
@future
public static void myFutureMethod(List<Id> recordIds) {
// Perform time-consuming operations or callouts here
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
// Process accounts
}
}
}
Why Use Future Methods?
What are the limitations of using future methods compared to other asynchronous processes like Queueable Apex, Batch Apex, and Schedulable Apex?
Future methods in Salesforce are useful for asynchronous processing, but they have several limitations compared to other asynchronous options like Queueable Apex, Batch Apex, and Schedulable Apex. Here's a detailed comparison of their limitations:
Limitations of Future Methods
Explain the difference between synchronous and asynchronous processes in Salesforce. When would you choose to use a future method over a synchronous method?
Synchronous Processes
trigger AccountTrigger on Account (after insert) {
for (Account acc : Trigger.new) {
// Perform synchronous operations
acc.Description = 'Updated by trigger';
}
update Trigger.new;
}
Asynchronous Processes
public class MyClass {
@future(callout=true)
public static void makeCallout(Id recordId) {
// Perform callout
HttpRequest req = new HttpRequest();
req.setEndpoint('https://example.com/api');
req.setMethod('GET');
HttpResponse res = new Http().send(req);
// Process response
}
}
When to Use Future Methods Over Synchronous Methods
Future methods are ideal in the following scenarios:
Example Scenario
Imagine you have a requirement to update related contacts whenever an account is updated, and then send an email notification to the account owner. The email notification is not critical and can be delayed.
trigger AccountTrigger on Account (after update) {
// Synchronous: Update related contacts
List<Contact> contactsToUpdate = [SELECT Id FROM Contact WHERE AccountId IN :Trigger.newMap.keySet()];
for (Contact con : contactsToUpdate) {
con.LastName = 'Updated by trigger';
}
update contactsToUpdate;
// Asynchronous: Send email notification
MyClass.sendEmailNotification(Trigger.newMap.keySet());
}
public class MyClass {
@future
public static void sendEmailNotification(Set<Id> accountIds) {
// Send email logic
}
}
What kinds of parameters supported in Future methods? Why sobject type parameters are not supported in Future methods?
Supported Parameters in Future Methods
Future methods in Salesforce can only accept primitive data types as parameters. These include:
SObjects (e.g., Account, Contact, Custom_Object__c) and collections of SObjects (e.g., List<Account>, Map<Id, Account>) are not supported as parameters in future methods. This limitation exists for several reasons:
What could be the workarounds for passing the SObject data?
Since SObjects cannot be passed directly to future methods, you can use the following workarounds:
Pass Record IDs: Pass the record IDs (Id or List<Id>) to the future method and query the records inside the method.
@future
public static void processAccounts(List<Id> accountIds) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
// Process accounts
}
Pass Field Values: Extract and pass specific field values as primitive data types or collections.
@future
public static void updateContacts(List<String> contactNames) {
// Process contact names
}
Serialize SObjects to JSON: Serialize SObjects to JSON strings and pass them as String parameters. Deserialize the JSON inside the future method.
@future
public static void processAccountData(String accountJson) {
Account acc = (Account) JSON.deserialize(accountJson, Account.class);
// Process account
}
Note: This approach should be used cautiously, as it can increase heap size and lead to governor limit issues.
Example of a Future Method with Primitive Parameters
Here’s an example of a future method that accepts primitive parameters:
public class AccountService {
@future
public static void updateContacts(List<Id> accountIds, String description) {
List<Contact> contactsToUpdate = [SELECT Id FROM Contact WHERE AccountId IN :accountIds];
for (Contact con : contactsToUpdate) {
con.Description = description;
}
update contactsToUpdate;
}
}
Can we recursively call one future method inside another future method?
No, you cannot recursively call a future method from another future method. Salesforce explicitly prevents this to avoid infinite loops, governor limit issues, and system performance degradation. If you attempt to call a future method from within another future method, Salesforce will throw a runtime exception with the error message:
System.AsyncException: Future method cannot be called from a future or batch method
This restriction ensures that future methods are used responsibly and do not create uncontrolled chains of asynchronous processes.
What happens if a future method is called recursively from a trigger?
If a future method is called recursively from a trigger, it can lead to unintended consequences, such as:
Example Scenario: Recursive Future Method Call from a Trigger
Consider a trigger that calls a future method, and the future method updates a record, which in turn re-triggers the same trigger:
trigger AccountTrigger on Account (after update) {
if (!System.isFuture()) { // Prevent recursion in future context
MyClass.processAccounts(Trigger.newMap.keySet());
}
}
public class MyClass {
@future
public static void processAccounts(Set<Id> accountIds) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
for (Account acc : accounts) {
acc.Name = acc.Name + ' - Processed';
}
update accounts; // This update re-triggers the AccountTrigger
}
}
In this example:
How can we prevent recursive future method calls?
To avoid recursive future method calls, use the following strategies:
Use a Static Boolean Flag: Use a static variable to track whether the future method has already been called in the current transaction.
public class MyClass {
public static Boolean isFutureMethodCalled = false;
@future
public static void processAccounts(Set<Id> accountIds) {
if (isFutureMethodCalled) return; // Exit if already called
isFutureMethodCalled = true;
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :accountIds];
for (Account acc : accounts) {
acc.Name = acc.Name + ' - Processed';
}
update accounts;
}
}
Check System.isFuture(): Use System.isFuture() to prevent future methods from being called in a future context.
trigger AccountTrigger on Account (after update) {
if (!System.isFuture()) { // Prevent recursion in future context
MyClass.processAccounts(Trigger.newMap.keySet());
}
}
What are the governor limits specific to future methods, and how do they differ from synchronous Apex limits?
Future methods in Salesforce run asynchronously and have different governor limits compared to synchronous Apex. These limits are designed to handle background processes more efficiently but still impose constraints to ensure system stability. Below is a detailed comparison of the governor limits specific to future methods and how they differ from synchronous Apex limits.
How to perform callouts using future method?
To perform callouts using future methods in Salesforce, you need to annotate the future method with @future(callout=true). This annotation tells Salesforce that the method will make an HTTP callout to an external system. Here’s a step-by-step guide on how to perform callouts using future methods:
Steps to Perform Callouts Using Future Methods
Example: Future Method for Making Callouts
public class CalloutService {
@future(callout=true) // Annotation to allow callouts
public static void makeCallout(Id recordId, String endpoint) {
try {
// Create an HTTP request
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint); // Set the endpoint URL
req.setMethod('GET'); // Set the HTTP method (GET, POST, etc.)
// Send the request
Http http = new Http();
HttpResponse res = http.send(req);
// Process the response
if (res.getStatusCode() == 200) {
// Success: Process the response body
String responseBody = res.getBody();
System.debug('Callout successful: ' + responseBody);
// Update the record with the response data (example)
Account acc = [SELECT Id, Name FROM Account WHERE Id = :recordId];
acc.Description = 'Callout response: ' + responseBody;
update acc;
} else {
// Handle non-200 responses
System.debug('Callout failed with status code: ' + res.getStatusCode());
}
} catch (Exception e) {
// Handle exceptions
System.debug('Callout error: ' + e.getMessage());
}
}
}
How to Call the Future Method
You can invoke the future method from a trigger, Apex class, or any other context. For example:
// Example: Calling the future method from a trigger
trigger AccountTrigger on Account (after insert) {
for (Account acc : Trigger.new) {
// Call the future method to make a callout
CalloutService.makeCallout(acc.Id, 'https://example.com/api');
}
}
Can I call the future method inside the batch class?
No, you cannot call a future method directly from a Batch Apex class because:
Why is the @future annotation necessary, and what happens if you omit it?
The @future annotation in Salesforce is used to designate a method as an asynchronous method. It tells the Salesforce runtime to execute the method in a separate thread, independent of the main transaction. This is crucial for handling long-running processes, callouts, or tasks that should not block the main transaction.
If you omit the @future annotation, the method will execute synchronously as part of the main transaction. This can lead to several issues:
Can future methods return values? If not, how would you handle scenarios where you need to return data from an asynchronous process?
No, future methods cannot return values. Future methods must have a void return type, meaning they cannot directly return data to the calling code. This is because future methods run asynchronously in a separate transaction, and the results are not immediately available to the main transaction.
If you need to return data from an asynchronous process, you can use the following strategies:
Use Callback Mechanisms: Implement a callback mechanism where the future method updates a record or triggers another process to handle the result.
Example: The future method updates a custom object or field with the result, and another process (e.g., a trigger, scheduled job, or platform event) processes the updated data.
@future
public static void processRecords(Set<Id> recordIds) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Processed by future method';
}
update accounts; // Update records with the result
}
Use Platform Events: Publish a platform event from the future method with the result data. Another process (e.g., a trigger or a subscriber) can listen for the event and handle the data.
@future
public static void processRecords(Set<Id> recordIds) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
List<My_Event__e> events = new List<My_Event__e>();
for (Account acc : accounts) {
// Create a platform event with the result
events.add(new My_Event__e(Account_Id__c = acc.Id, Description__c = 'Processed by future method'));
}
EventBus.publish(events); // Publish the events
}
Trigger to handle the event:
trigger MyEventTrigger on My_Event__e (after insert) {
for (My_Event__e event : Trigger.new) {
// Process the event data
System.debug('Account ID: ' + event.Account_Id__c + ', Description: ' + event.Description__c);
}
}
Use Queueable Apex: Queueable Apex is a more flexible alternative to future methods and can be chained to pass data between jobs.
public class MyQueueable implements Queueable {
private Set<Id> recordIds;
public MyQueueable(Set<Id> recordIds) {
this.recordIds = recordIds;
}
public void execute(QueueableContext context) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Processed by Queueable job';
}
update accounts;
// Chain another job if needed
System.enqueueJob(new NextQueueableJob(accounts));
}
}
Use Batch Apex: For large-scale data processing, use Batch Apex to handle asynchronous operations and store results in custom objects or fields.
public class MyBatchClass implements Database.Batchable<sObject> {
public Database.QueryLocator start(Database.BatchableContext context) {
return Database.getQueryLocator([SELECT Id, Name FROM Account]);
}
public void execute(Database.BatchableContext context, List<Account> scope) {
for (Account acc : scope) {
acc.Description = 'Processed by Batch Apex';
}
update scope; // Store the result in the records
}
public void finish(Database.BatchableContext context) {
// Post-processing logic
}
}
By leveraging these strategies, you can effectively handle asynchronous processes and manage data flow in Salesforce.
For more questions: Interview Questions - Future Method in Salesforce - Part 2
Closing Note: Thank you for taking the time to read my article! I’m passionate about Salesforce development and always eager to explore new challenges and opportunities. If you found this article insightful or would like to discuss Salesforce, innovative solutions, or potential collaborations, I’d love to connect!
P.S. I’m currently on the verge of completing my Master’s in Computer Science from California State University, Long Beach, and I’m actively seeking new opportunities as a Salesforce Developer in the USA. If you or your organization are looking for someone with a strong Salesforce skill set, a solid academic foundation, and a drive to deliver impactful solutions, feel free to reach out. Let’s connect and see how we can create something amazing together!
You can reach me via LinkedIn or at [email protected]. Looking forward to connecting with fellow professionals and salesforce enthusiasts!
Sr. Salesforce Developer|| 10x Certified || 10x Superbadge || Double Star Ranger || Former Research Intern, Microsoft Research Lab Pvt.
3 周Very informative