Asynchronous Apex

Asynchronous Apex

This type of Apex is used to run the process in a separate thread in background without the user having to wait for this task to finish.
There are different ways / framework that are available in force.com to run an Apex asynchronously.

1. Future Methods
2. Queueable Apex
3. Batch Apex
4. Scheduled Apex

What is Asynchronous?

An asynchronous process is a process or function which does not require interaction with a user. It can be used to execute a task “in the background” without the user having to wait for the task to finish.

When to run an apex asynchronously? Why it is required?
Consider an example where we have five different process A, B, C and D that run sequentially (synchronously).
Since all these process run sequentially, each process has to wait for one another process to be completed.
See the cases below
Process       Execution Time         Min Waiting Time          Dependencies
A                             1 min                                   0                                   No

B                             60 min                                0                                   Yes

C                             2 min                                  61                                  No

D                             1 min                                  62                                  No

The above scenario clearly shows that the process C and D do not have any dependencies but have to wait for the processes A & B to complete. This situation require the processes C and D to be executed asynchronously in a separate thread to save the execution time.

What to use when?
Future Methods 

  1. When you have long running method and need to prevent delaying an Apex transaction.

2. To avoid mixed DML exception.

Queueable Apex

  1. To chain jobs
  2. To start a long-running operation and get an ID for it.

Batch Apex

Long running jobs with large data volumes can run in number of batches using batch apex. Example, the database maintenance jobs, update a field for large volume of records etc.,

Scheduled Apex

To schedule an apex to be run on a specific schedule.

What is future method?

It runs the process asynchronously. The governor limit is also higher than the synchronous limit. Such as SOQL and heap size limits. SOQL is 100 for synchronous and 200 for asynchronous.

Future method is also used to prevent mixed DML exception. The mixed DML exception would occur when we perform DML on both setup and non-setup sObjects in a transaction.

Setup sObjects are those objects related to users permissionS.

Non-Setup are those sObjects used to store business information such as Account, Contact, Opportunity and other generic sObjects come under Non-Setup sObject category.

DML operations on certain sObjects, sometimes referred to as setup objects, can’t be mixed with DML on other sObjects in the same transaction. This restriction exists because some sObjects affect the user’s access to records in the org.

You must insert or update these types of sObjects in a different transaction to prevent operations from happening with incorrect access-level permissions.

For example, you can’t update an account and a user role in a single transaction. Inserting a user with a non-null role must be done in a separate thread from DML operations on other sObjects. This example uses a future method to achieve this

Please note that deleting a DML operation has no restrictions.

This scenario requires a transaction to run seperately using future method as follows.

How to define a method as a future method?
Use annotation @future to mark a method as a future method as follows.

global class MySampleClass { 
     @future 
    public static void myFutureMethod() { 
          // Perform some operations 
     }
}

Pre-requisite to define a future method.
1. The method must be a static and void.
2. If parameterized method, then that should be a primitive, array of primitive or collection of primitives.
The paramaters other than primitive (object or sObjects) will not be accepted in future method.

To work with sObjects, we can pass list of IDs and query using these IDs.

global class FutureMethodRecordProcessing { 
        @future 
        public static void processRecords(List<ID> recordIds) { 
             // Get those records based on the IDs 
             List<Account> accts = [SELECT Name FROM Account WHERE Id IN :recordIds]; 
           // Process records 
       } 
}

How to define a future method to callout an external service?.
Use @future annotation along with an additional parameter callout=true as follows,

global class FutureMethodExample { 
         @future(callout=true) 
          public static void getStockQuotes(String acctName) { 
                // Perform a callout to an external service 
          } 
}

This way, a process will make a callout an external service to run asynchronously.

Hence, future method is used
1. For executing long-running operations, such as callouts to external Web services or
2. Any operation you’d like to run in its own thread, on its own time.
3. To isolate DML operations on different sObject types to prevent the mixed DML error.

Each future method is queued and executes when system resources become available. That way, the execution of your code doesn’t have to wait for the completion of a long-running operation. A benefit of using future methods is that some governor limits are higher, such as SOQL query limits and heap size limits.

Example where future method is used to avoid Mixed DML.
Scenario where DML performed for both setup and non-setup together in same transaction.

public class MixedDMLFuture {
        public static void useFutureMethod() { 
               // First DML operation 
               Account a = new Account(Name='Acme'); 
               insert a; 

              // Other DML Operation
              Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; 
              UserRole r = [SELECT Id FROM UserRole WHERE Name='COO']; 
             // Create new user with a non-null user role ID 
             User u = new User(alias = 'ranvk', email='sfdcmeet@gmail.com',                   
                            emailencodingkey='UTF-8', lastname='krish',languagelocalekey='en_US',     
                            localesidkey='en_US', profileid = p.Id, userroleid = r.Id,  
                            timezonesidkey='America/Los_Angeles',  
                            username='ranjithvk@sfdcmeet.com'); 
            insert u; 
       } 
}

Execute the above method useFutureMethod() in developer console using anonymous block.
MixedDMLFuture.useFutureMethod();

This would give a Mixed DML exception.

How to avoid Mixed DML using future method.? Using future method, we can process one of these DML in separate thread asynchronously as follows.

1. Future Method to insert an user.

public class Util { 
      @future 
       public static void insertUserWithRole(String uname, String al, String em, String lname) { 
               Profile p = [SELECT Id FROM Profile WHERE Name='Standard User']; 
               UserRole r = [SELECT Id FROM UserRole WHERE Name='COO']; 
               // Create new user with a non-null user role ID 
               User u = new User(alias = al, email=em, emailencodingkey='UTF-8',   
                              lastname=lname, languagelocalekey='en_US', localesidkey='en_US', 
                              profileid = p.Id, userroleid = r.Id, timezonesidkey='America/Los_Angeles',  
                              username=uname); 
               insert u; 
       } 
}

The below class calls the above method after a DML against non-setup sObject.

public class MixedDMLFuture { 
       public static void useFutureMethod() { 
            // First DML operation 
             Account a = new Account(Name='Acme'); 
             insert a; 
             // This next operation (insert a user with a role)  can't be mixed with the previous    
            insert        
            //unless it is within a future method. 
            // Call future method to insert a user with a role. 
             Util.insertUserWithRole( 'ranjithvk@sfdcmeet.com', 'ranvk', 'sfdcmeet@gmail.com', 
                                                    'krish'); 
         } 
}

You can invoke future methods the same way you invoke any other method. However, a future method can’t invoke another future method.

This example shown how to use future to avoid Mixed DML. Hence just remove the @future annotation for the method Util and execute useFutureMethod to see the Mixed DML exception. That would signify the use of future method in this scenario. Each future method is queued and executes when system resources become available

Testing Future Methods

the methods startTest() and stopTest() are used to test the block of code asychronously. When stopTest is executed, all asynchronous processes are run synchronously.

@isTest 
private class MixedDMLFutureTest { 
       @isTest 
        static void test1() { 
               User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId()]; 
               // System.runAs() allows mixed DML operations in test context 
               System.runAs(thisUser) { 
                    // startTest/stopTest block to run future method synchronously 
                    Test.startTest(); 
                    MixedDMLFuture.useFutureMethod(); 
                   Test.stopTest();
               } 

              // The future method will run after Test.stopTest(); 
              // Verify account is inserted 
               Account[] accts = [SELECT Id from Account WHERE Name='Acme']; 
               System.assertEquals(1, accts.size()); 
               // Verify user is inserted 
               User[] users = [SELECT Id from User where username='mruiz@awcomputing.com']; 
               System.assertEquals(1, users.size());
        }
}