Salesforce has an annotation @future that we can use defining a method making its execution as asynchronous.
Future method must be static methods, and can only return a void type and primitive data types as parameter.
@future public static void aFutureMethod1(){ System.debug('---Future method: aFutureMethod1() is running'); }
- Why public: If it is private then this will not be accessible at the time of execution. Internal members of a class can only access its private members, nobody else.
- Why Static: Salesforce Apex engine does not need to instantiate the Class object rather will call with class name when resources are free.
- Why Void return: Ask the question – After completion of future method execution where it will return the result/response? No answer, right? Because that transaction thread where from future method was called may be closed.
- Why primitive data types as parameter, why not any sObjects: Each data type is a definition. If non-primitive data type is used that may not sometime mismatch with the sObject definition, in case object structure been changed.
Few points to ponder
- Future methods won’t necessarily execute in the same order they are called. [it’s possible that two future methods could run concurrently, which could result in record locking if the two methods were updating the same record.]
- Future methods can’t be used in Visualforce controllers in setter, getter, nor in the constructor.
- We can’t call a future method from another future method. Assume, a future method called another future method from its body. Salesforce engine calls a future method from a synchronous thread and adds that request to the asynchronous queue. When that calling future method starts execution it executes in asynchronous thread and hence can not add that to the asynchronous queue. Better to check through isFuture and then call a future method. Occurs exception System.AsyncException: Future method cannot be called from a future or batch method
- Future methods can not be called from the batch class. Error occurs: Future method cannot be called from a future or batch method:
- Future methods can be called from the Queueable Apex.
- Queueable Apex can be called from Future methods.
- Batch classes can be called from Future methods.
- The getContent() and getContentAsPDF() methods can’t be used in methods with the @future annotation.
- We’re limited to 50 future calls per Apex invocation, and there’s an additional limit on the number of calls in a 24-hour period.
When to use future methods
- Long-running method: Block of statements for a functionality that takes good amount of time but runs/executes independently and does not need to return anything for further processing in main thread where from it is called, in that case future method is best fitted. It gets separated out from main thread for future execution without delaying main thread apex transaction with no impact.
- Callouts to external Web services: Sending request to external system and then wating for response and once get response then return to main thread is actually takes time. Even in this kind of cases, triggers/classes keep opening up database connection that is not acceptable as per good practices. Hence, call that part through future method.
- Mixed DML Error resolution: We can use future method to segregate DML operations and bypass the mixed save DML error.
At the time of usage, we should follow few best practices like below:
- Try to bundle all feature/callouts together from the same future method, rather than using a separate future method for each callout/feature.
- Conduct thorough testing at scale. Test that a trigger enqueuing the @future calls is able to handle a trigger collection of 200 records. This helps determine if delays may occur given the design at current and future volumes.
- Consider using Batch Apex instead of future methods to process large number of records asynchronously. This is more efficient than creating a future request for each record.
Hands on
Create 3 classes as mentioned below. Next you need to run each one from Developer Console for your understanding of who can call whom. Comment and uncomment the execution lines and see the magic responses.
1. AsynchronousStudyFuture: Create this class in your personal org coping below code. This class has future methods defined.
public class AsynchronousStudyFuture { @future public static void aFutureMethod1(){ System.debug('---Future method: aFutureMethod1() is running'); } @future public static void aFutureMethod2(){ System.debug('---Future method: aFutureMethod2() is running'); aFutureMethod1(); //Calling another future method } public static void justChecking(){ System.debug('---Running from AsynchronousStudyFuture.justChecking()'); //aFutureMethod1();//Basic calling of a future method //System.enqueueJob(new AsychronousStudyQueueable());//Enqueueing queueable job Database.ExecuteBatch(new AsynchronousStudyBatch()); //aFutureMethod2();//Check for error } }
2. AsynchronousStudyBatch: This is just a simple batch class. For execution purpose we have written a SOQL that you can ignore.
global class AsynchronousStudyBatch implements Database.Batchable<sObject> { global Database.QueryLocator start(Database.BatchableContext bc) { String strQuery = 'SELECT Id FROM Account'; return Database.getQueryLocator(strQuery); } global void execute(Database.BatchableContext bc, List<sObject> records){ System.debug('---From batch class AsynchronousStudyBatch execute()'); System.enqueueJob(new AsychronousStudyQueueable());//Enqueueing queueable job //AsynchronousStudyFuture.justChecking(); } global void finish(Database.BatchableContext bc){ // execute any post-processing operations } }
3. AsychronousStudyQueueable: This is a simple queueable apex class having only execute() method.
public class AsychronousStudyQueueable implements Queueable { public void execute(QueueableContext context) { System.debug('---Running from Queueable job. AsychronousStudyQueueable class execute'); //AsynchronousStudyFuture.justChecking(); //Database.ExecuteBatch(new AsynchronousStudyBatch()); //System.enqueueJob(new AsychronousStudyQueueable()); } }
Developer Console
System.enqueueJob(new AsychronousStudyQueueable()); Database.ExecuteBatch(new AsynchronousStudyBatch()); AsynchronousStudyFuture.justChecking();