Tuesday, October 26, 2021

Lets learn Future method today!

In this post, we will try to cover the Future method. 

Note: Here we will not talk about how Queueable is better than the future method, rather we will stick to knowing about the Future method.


First things first, What is Async Apex and why do we need Async Apex in the first place? 

Async Apex is simply used to run your transaction in a different thread at a later point in time when resources are available, without your user actually waiting for it to finish. 

Assume if we could make callouts from triggers, in synchronous transaction user would have to wait for the response to come back before he could actually move ahead and that would be a terrible experience.


Luckily we have Asynx Apex methods at our disposal for such things.

There could be a few more scenarios where we might want to use Async Apex.

For example : 

  • Making callouts to an external system.  
  • Mixed DML error (We will learn later with an example) 
  • Any complex calculation which might require more governor limits.


Future methods are more like fire and forget methods as we don't know when this method will be actually executed in the future. And unlike other async apex methods, it doesn't return a job Id which we could have used to track the status.

Syntax 



Important points : 

  • We need to use @future annotation to mark any method as future.
  • We can't have any return type on future methods because these methods are executed when resources are available in future time, if we were to have a return type then the transaction would stop there and wait for returned value.
  • We can only pass primitive arguments to a future method for example String, Integer, List<String> etc. 
  • We cant pass non-primitive arguments like Sobjects as the state of records could change by the time future method is actually executed and we could end up overwriting the record. 
  • The best way would be to pass List<Id> and then query them to get the latest copy of the record.
  • The order of execution of multiple future methods is not defined. 
  • We cant call a future method from another future method ie chaining is not allowed.If you try to do that you will face an error as "Future method cannot be called from a future or batch method"
  • We also can't call from batch class, hence its always advisable to put a check before calling future method like below : 
             If( ! System.isFuture() && !System.isBatch()){
                  // Call your future method here
               }
  • When looking at debug logs you will see "FutureHandler" in the operation for debug logs, makes it easy to look at debug for future method specifically.
  • We can call a future method from Queueable Class and can also call queueable class from future method, well if you are thinking can we also do Future-> Queueable -> Future then yes we can do it and no error will be thrown.

The following is a skeletal example of a future method that makes a callout to an external service. Notice that the annotation takes an extra parameter (callout=true) to indicate that callouts are allowed


Did you know?    

  • As we discussed it's not allowed to pass non-primitive data types but still if we want to, we can simply serialize the data which will convert it into String then pass into the method.
Note: Not advisable to do, you should always pass the record id and then query to get a fresh copy.
  • Similarly, if you want to pass wrapper to your future method you can serialize it and pass it then deserialize it inside the future method.
  • Future methods are only called if a sync transaction succeeds, in case a sync transaction fails, the future method will never be even executed.
  • But in case of future method fails sync transaction will not roll back, considering both are running in different threads any exception in the future transaction will only roll async transaction.
  • Similarly in the case of the future method is being called inside another future method sync transaction will succeed and the only async transaction will fail and rollback. 
In the below example, the account number will get updated on all the records but contact insertion will fail in the future method as another future method is being called and hence future transaction will roll back



Let's discuss Mixed DML errors in detail now, below is what error looks like
"System.DmlException: Insert failed. First exception on row 0; first error: MIXED_DML_OPERATION"

This error occurs when you try to do DML on both setup and non-setup objects in a single transaction
Now comes the question what are setup and non-setup objects actually? 

Any object which impacts sharing settings in the system ie insert or update of any entity which will lead to re-calculation of sharing is considered as a setup object. For example User,Group,QueueSobject etc.

Note : If you insert a User record with Role defined will cause this error while a User record without a Role will not lead to a Mixed DML error as sharing will not be impacted if the Role is blank.

Similarly, if you just insert a group in apex along with transactional objects it will not cause issues unless you also insert Group members, as blank group ie without any member will never impact sharing.

All other transactional objects like Account, Contact are considered as non-setup object.


Example Code : 

Inserting user and Account record.



If you run this you will face a Mixed DML error. We can resolve it by simply rewriting it and moving user creation to a future method.


For limits related to Future methods please go through Salesforce document

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_invoking_future_methods.htm


Thanks for reading...

If you liked the post do support me by buying a pizza for me :D