Interview Questions - Future Method in Salesforce - Part 2
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
How do you handle exceptions in future methods? Can you use try-catch blocks in future methods, and how would you log errors for debugging?
Handling exceptions in future methods is crucial because unhandled errors can cause the method to fail silently, making debugging difficult. You can use try-catch blocks in future methods to catch and handle exceptions gracefully. Additionally, you can log errors for debugging purposes using debug statements, custom objects, or Salesforce's logging mechanisms.
How to Handle Exceptions in Future Methods
Example of try catch block:
public class MyFutureClass {
@future
public static void processRecords(Set<Id> recordIds) {
try {
// Perform operations
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Processed by future method';
}
update accounts;
} catch (Exception e) {
// Log the error
System.debug('Error: ' + e.getMessage() + ' | Stack Trace: ' + e.getStackTraceString());
}
}
}
Explanation
Best Practices for Exception Handling in Future Methods
Salesforce Logging Tools
Explain how you would use future methods to integrate Salesforce with external systems. What are the potential pitfalls of this approach?
Using Future methods to integrate Salesforce with external systems is a common approach for handling asynchronous callouts. Future methods allow you to make HTTP callouts to external systems without blocking the main transaction. However, there are some potential pitfalls to be aware of. Let’s break it down:
Example: Future Method for External Integration
public class ExternalSystemIntegration {
@future(callout=true)
public static void callExternalSystem(Set<Id> recordIds) {
// Query records to process
List<Account> accounts = [SELECT Id, Name, External_Id__c FROM Account WHERE Id IN :recordIds];
// Make callout to external system
Http http = new Http();
HttpRequest request = new HttpRequest();
request.setEndpoint('https://api.externalsystem.com/update');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
for (Account acc : accounts) {
// Prepare the request body
String requestBody = JSON.serialize(new Map<String, String>{
'name' => acc.Name,
'externalId' => acc.External_Id__c
});
request.setBody(requestBody);
// Send the request
HttpResponse response = http.send(request);
// Handle the response
if (response.getStatusCode() == 200) {
System.debug('Callout successful for Account: ' + acc.Id);
} else {
System.debug('Callout failed for Account: ' + acc.Id + '. Status Code: ' + response.getStatusCode());
}
}
}
}
Potential Pitfalls of Using Future Methods for Integration
You have a requirement to update thousands of records asynchronously. Would you use future methods, batch Apex, or Queueable Apex? Justify your choice.
When updating thousands of records asynchronously in Salesforce, the choice between Future methods, Batch Apex, and Queueable Apex depends on the specific requirements, such as the volume of data, complexity of the operation, and governor limits. Here's a detailed comparison and justification for each option:
Future Methods
Queueable Apex
Batch Apex
So for updating thousands of records asynchronously, Batch Apex is the best choice because:
If the requirement involves smaller datasets or chaining multiple asynchronous processes, Queueable Apex could be a viable alternative. However, for bulk updates, Batch Apex is the most robust and scalable solution.
What is a Mixed DML error? How can we use future method to avoid mixed DML error?
A Mixed DML error occurs in Salesforce when you try to perform Data Manipulation Language (DML) operations on setup objects (e.g., User, Group, Profile) and non-setup objects (e.g., Account, Contact, Custom_Object__c) in the same transaction. Salesforce enforces this restriction to maintain data consistency and prevent conflicts between setup and non-setup objects.
Example: Mixed DML errors typically occur in scenarios like:
User u = new User(Id = UserInfo.getUserId(), FirstName = 'Test');
update u; // DML on setup object (User)
Account acc = new Account(Name = 'Test Account');
insert acc; // DML on non-setup object (Account)
This code will throw the following error:
System.DmlException: Mixed DML operation [User] and [Account] not supported.
To avoid Mixed DML errors, you can use future methods to separate the DML operations on setup and non-setup objects into different transactions. Future methods run asynchronously, allowing you to perform DML on setup objects in one transaction and non-setup objects in another.
Steps to Use Future Methods to Avoid Mixed DML Errors
Example: Using Future Methods to Avoid Mixed DML Errors
public class MixedDMLHandler {
// Main method to handle non-setup object DML
public static void updateAccountAndUser() {
// DML on non-setup object (Account)
Account acc = new Account(Name = 'Test Account');
insert acc;
// Call future method to handle setup object DML (User)
updateUserAsync(UserInfo.getUserId(), 'New First Name');
}
// Future method to handle setup object DML (User)
@future
public static void updateUserAsync(Id userId, String newFirstName) {
User u = new User(Id = userId, FirstName = newFirstName);
update u; // DML on setup object (User)
}
}
领英推荐
Explanation
By separating the DML operations into different transactions, the Mixed DML error is avoided.
Once I call a future method, How can I trace its execution?
Tracing the execution of a Future method in Salesforce can be challenging because Future methods run asynchronously and do not return values or propagate errors back to the calling code. However, there are several strategies you can use to monitor and debug the execution of Future methods:
Use Debug Logs
Example:
public class FutureMethodExample {
@future
public static void myFutureMethod(Set<Id> recordIds) {
System.debug('Future method started with record IDs: ' + recordIds);
// Perform logic
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Updated by Future method';
}
update accounts;
System.debug('Future method completed successfully.');
}
}
Use Custom Objects for Logging
Example:
public class FutureMethodExample {
@future
public static void myFutureMethod(Set<Id> recordIds) {
// Log start of execution
Async_Log__c log = new Async_Log__c(
Status__c = 'Started',
Record_Ids__c = String.join(new List<Id>(recordIds), ',')
);
insert log;
try {
// Perform logic
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Updated by Future method';
}
update accounts;
// Log success
log.Status__c = 'Completed';
update log;
} catch (Exception e) {
// Log error
log.Status__c = 'Failed';
log.Error_Message__c = e.getMessage();
update log;
}
}
}
How to test a future method?
Testing a Future method in Salesforce requires special considerations because Future methods run asynchronously and do not execute immediately. To ensure your Future method is tested properly, you need to use the Test.startTest() and Test.stopTest() methods to force the asynchronous code to execute during the test.
Steps to Test a Future Method
Create the Future Method: Ensure your Future method is properly defined with the @future annotation.
public class FutureMethodExample {
@future
public static void myFutureMethod(Set<Id> recordIds) {
List<Account> accounts = [SELECT Id, Name FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
acc.Description = 'Updated by Future method';
}
update accounts;
}
}
Write the Test Class: Use Test.startTest() and Test.stopTest() to force the Future method to execute synchronously during the test. Verify the results after the Future method completes.
@isTest
public class FutureMethodExampleTest {
@isTest
static void testMyFutureMethod() {
// Create test data
Account testAccount = new Account(Name = 'Test Account');
insert testAccount;
// Call the Future method
Test.startTest();
FutureMethodExample.myFutureMethod(new Set<Id>{testAccount.Id});
Test.stopTest();
// Verify the results
Account updatedAccount = [SELECT Id, Description FROM Account WHERE Id = :testAccount.Id];
System.assertEquals('Updated by Future method', updatedAccount.Description, 'The Future method did not update the account description.');
}
}
Key Points to Remember
By following these steps and best practices, you can effectively test Future methods in Salesforce.
Can we pass wrapper to future method?
No, you cannot directly pass a wrapper class object to a Future method in Salesforce. Future methods have specific limitations on the types of parameters they can accept. According to Salesforce documentation, Future methods can only accept primitive data types (e.g., Integer, String, Boolean, Id, etc.) or collections of primitive data types (e.g., List<String>, Set<Id>, Map<Id, String>).
Wrapper classes, which are custom Apex classes, are not primitive data types and therefore cannot be passed directly to a Future method. Future methods are executed asynchronously in a separate transaction. Salesforce serializes the parameters passed to Future methods to store them in the database temporarily. Since wrapper classes are complex objects, they cannot be serialized and deserialized in a way that Salesforce supports for Future methods.
Workarounds to Pass Wrapper-Like Data to Future Methods
Pass Primitive Data Types and Reconstruct the Wrapper
Example:
// Wrapper class
public class MyWrapper {
public String name;
public Integer age;
public Boolean isActive;
}
// Future method
public class FutureMethodExample {
@future
public static void myFutureMethod(String name, Integer age, Boolean isActive) {
// Reconstruct the wrapper
MyWrapper wrapper = new MyWrapper();
wrapper.name = name;
wrapper.age = age;
wrapper.isActive = isActive;
// Use the wrapper
System.debug('Wrapper: ' + wrapper);
}
}
// Calling the Future method
MyWrapper wrapper = new MyWrapper();
wrapper.name = 'John';
wrapper.age = 30;
wrapper.isActive = true;
FutureMethodExample.myFutureMethod(wrapper.name, wrapper.age, wrapper.isActive);
Serialize the Wrapper to JSON and Pass as a String
Example:
// Wrapper class
public class MyWrapper {
public String name;
public Integer age;
public Boolean isActive;
}
// Future method
public class FutureMethodExample {
@future
public static void myFutureMethod(String wrapperJson) {
// Deserialize the JSON string into the wrapper
MyWrapper wrapper = (MyWrapper) JSON.deserialize(wrapperJson, MyWrapper.class);
// Use the wrapper
System.debug('Wrapper: ' + wrapper);
}
}
// Calling the Future method
MyWrapper wrapper = new MyWrapper();
wrapper.name = 'John';
wrapper.age = 30;
wrapper.isActive = true;
String wrapperJson = JSON.serialize(wrapper);
FutureMethodExample.myFutureMethod(wrapperJson);
Use Record IDs and Query Inside the Future Method
Example:
// Wrapper class
public class MyWrapper {
public String name;
public Integer age;
public Boolean isActive;
}
// Future method
public class FutureMethodExample {
@future
public static void myFutureMethod(Set<Id> recordIds) {
// Query records and reconstruct the wrapper
List<Account> accounts = [SELECT Id, Name, Age__c, Is_Active__c FROM Account WHERE Id IN :recordIds];
for (Account acc : accounts) {
MyWrapper wrapper = new MyWrapper();
wrapper.name = acc.Name;
wrapper.age = Integer.valueOf(acc.Age__c);
wrapper.isActive = acc.Is_Active__c;
// Use the wrapper
System.debug('Wrapper: ' + wrapper);
}
}
}
// Calling the Future method
Account acc = new Account(Name = 'John', Age__c = 30, Is_Active__c = true);
insert acc;
FutureMethodExample.myFutureMethod(new Set<Id>{acc.Id});
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!