Future Vs Queueable Apex

What is the difference between future method and Queueable Job in Apex? Join us to learn about Future Vs Queueable Apex. Instead of directly jumping on to learn about the future and queueable apex first we will learn about what is synchronous process and why we need use asynchronous apex.

Synchronous Process

In simple words we can say executing a class based on single thread that is nothing but execute one method at a time either using top down or bottom to top approach.

Example: Imagine you have a requirement like when you insert a contact or delete a contact update the account with number of contacts and then you need to perform web service callout for that account. Using the synchronous apex is no-no situation because we always need to deal with the governor limits and apex CPU time. So how can we solve this situation.

Can we solve this problem using the asynchronous process?

Asynchronous process

The answer is yes, asynchronous process will execute in its own thread it is independent. So how many asynchronous flavors we have in salesforce?

I would say as of today we have total of 4 flavors. Out of 4 do we have any asynchronous process that is annotation based. So, we need not to write a separate class and handle the asynchronous process.

Asynchronous process in Salesforce

Do not worry we have that in Salesforce. It is nothing but @future annotation how cool it is right. Now we are ready to deep dive into learn about the future asynchronous process

So, what exactly is @future annotation?

@future annotation will start when the resources are available, and they will run on their own thread. Now you will get a question like in which situation I can use @future?

That is actually a good question

  1. When we need to do a web service callout after the DML operation.
  2. When you want to perform the calculations as soon as the resource are available.
  3. Before going to this user case can we solve the mixed DML operation problem by using this? The answer is yes, we can solve the mixed DML problem by using the @future annotation.

Enough to the theory now let’s take use case and see how can use the @future annotation.

Use case:

  1. Create a field Account “Number of Contacts” with data type Number. Update the field using @future annotation when the contact is created or Deleted.

Solution:

With the above use case what we need to do?

  1. We need to create a field on account
  2. We need to write a trigger on Contact and fire the trigger for insert and delete operations.
    Based on this information can you do of your own? If yes, that is great. Go ahead and do it. If you are
    new to apex no problem, you can check the code below.

Trigger on Contact: ContactTrigger

trigger ContactTrigger on Contact (after insert, after delete) {

	// After events
	if(Trigger.isafter) {
		if(Trigger.isInsert) {
			AccountProcessor.onAfterSave(Trigger.New);
		}
		if(Trigger.isDelete) {
			AccountProcessor.onAfterSave(Trigger.Old);
		}
	}
}

Apex class: AccountProcessor

public with sharing class AccountProcessor {
	//== Method to execute on after Insert of contact
	public static void onAfterSave (List<Contact> contacts) {
		// Set to hold the account Id's from Contact
		Set<Id> acctIds = new Set<Id>();
		// Loop to iterate over the list of contacts
		for(Contact con : contacts) {
			if(con.AccountId != null) {
				acctIds.add(con.AccountId);
			}
		}
		if(acctIds.size() > 0 && acctIds != null) {
			updateAccount(acctIds);
		}
	}

	//== Method to update the account based on Number of contacts
	@future(callout=false)
	private static void updateAccount(Set<Id> accIds) {
		// Qeury to fetch the account records based on AccountId from contact
		List<Account> accnts = [SELECT ID, Name, Number_of_Contacts__c, (SELECT Id FROM Contacts)
										FROM Account
										WHERE ID = :accIds];
										System.debug('query results : ' +accnts.size());

		// Loop to iterate over the list of account records from Query results
		for(Account acc : accnts) {
			List<Contact> cons = acc.Contacts; // List to hold the contacts that related to account
			acc.Number_of_Contacts__c = cons.size();
			System.debug('Account:'+ acc.Name + ' has ' + acc.Number_of_Contacts__c + ' Contact childs');
		}

		try {
			update accnts;
		} catch(DMLException e) {
			throw new stringException('Faile to update the accounts : '+e.getMessage());
		}
	}
}

In that method we are preparing the set of Id’s why not list of Id’s? More than one contact will be related to the same account if you are doing the file loads right so set will hold the unique Id’s. If the set is not null, then we are going to perform the actual logic. Why we need to perform that check? Don’t worry I’ll explain why. Account Id is not a required field so we can create individual contacts and we can link them to the parent account later time. So, what if the whole file that we are using is not having single account Id.

Then do we need to query on account? No right, now you got it. In the downstream logic we are performing the query on account with the Id’s that we collected from the list of contacts and based on the account Id’s we are getting the up to time, wait what why not up to date? contacts and assigning that value to the account field after that perform the DML operation using try catch block
to handle the exceptions.

Things to Remember

Things to Remember when using @future annotation

  1. Future method should return void and it should be declared as static.
  2. Can we pass objects as a parameter to the future method? No, we cannot pass the objects (custom, standard) as parameter. We can only pass the Id’s as a parameter, primitive data type or collection of primitive data types as a parameter.
  3. Why we cannot pass the object (custom, standard) as a parameter? Remember we are working with asynchronous apex they will execute only when the resources are available so if we use the object as a parameter then the system will not hold the current data.
  4. Did future method execute in the same order they called? The answer is no, as they are not guaranteed to execute in the same order. So, can we call it a limitation? Yes, we can overcome this by using the queueable apex.
  5. Will future methods run concurrently which means updating the same record by 2 future methods at a same time from different sources or the same source? Yes, and which could result in locking the record. so, need to incredibly careful while dealing with future methods.
  6. Do I have to take any special care while writing the test class for future methods? Yes, you need to enclose the code between start and stop test methods.
  7. Can I use future method for bulk transactions? The answer will not be likeable, but it is good to avoid future and good to use batch processing.
  8. What is the limit on future calls per apex invocation? 50, additionally there are limits base on the 24-hour time frame.
  9. Can we user future method in visual force controller constructors? The answer is no.
  10. Can I call a future method from another future method? No, you cannot call one future method from another future method.

Queueable Apex

Before jumping on to queueable apex lets try to remember some of the limitations that future method is having.

  1. Can we pass objects, apex types as a parameter to the future method? No, we cannot pass them as arguments. So, how can I do that?
  2. Can I monitory future jobs? No, it is a method that resides in an apex class and it will execute whenever the resources are available. So, how can we resolve this? Is there any way that I can implement asynchronous jobs and monitor them?
  3. Can we implement chain process? Forget with future because we cannot call future method from another future method. So, how can we resolve this?

The only solution to the above 3 problems is implementing the queueable apex. What is queueable apex and how can me implement them. Oh! Wait too many questions.

Queueable apex is hybrid class. Why you are calling it as hybrid class? Yes, I mean to say it is designed in a way by combining the batch apex and future apex. So, that means do we have to again write the start, execute, and finish methods like the batch. As I said it is a combination not the exact replica. No, you don’t need to write start and finish methods. All you need is execute method. How cool it is right.

Use Case

You need insert a contact for each account for a specific state.

public class AddPrimaryContact implements Queueable {
	// Declaring the varibales
	private Contact contact;
	private String state;

	//== Defualt constructor with parameters as account and state value
	public AddPrimaryContact(Contact contactRecord, String stateValue) {
		this.contact = contactRecord;
		this.state = stateValue;
	}

	public void execute(QueueableContext context) {
		List<Account> accounts = getAccounts(state);
		// Condition to check for the List is empty
		if(accounts.size() > 0) {
			List<Contact> updateContacts = new List<Contact>();
			// Loop to iterate over the list of Accounts
			for(Account account : accounts) {
				Contact con = new Contact();
				con.AccountId = account.Id;
				updateContacts.add(con);
			}
			// Condition to check if the contact list is empty
			if(updateContacts.size() > 0) {
				Database.SaveResult[] results = Database.insert(updateContacts, false);
				// Loop to iterate over the DML Operation results
				for(Database.SaveResult result : results) {
					if(result.isSuccess()) {
						System.debug('Successufully inserted the contact. Contact Id : ' + result.getId());
					} else {
						for(Database.Error error : result.getErrors()) {
							System.debug('Contact failed to Insert with error code : '
							+ error.getStatusCode() +
							' with error Message : ' +error.getMessage());
						}
					}
				}
			}
		}
	}
	// Method to fetch the List of account records based on the State value.
	public static List<Account> getAccounts(string state) {
		return [SELECT ID, Name,
				(SELECT Id, FirstName, LastName FROM Contacts)
				FROM Account
				WHERE BillingState = :state];
	}
}

In the class we are passing the parameters as contact and the state value. The default constructor will receive it and we created 2 global variables one for contact and the other for the state value to hold and to use them in our downstream logic. The execute method is first doing a query on account object and bringing all the records that matches with the state value then we are checking if there are any query results if so then we are iterating over the query results and then we are creating the new contact for those accounts.

Here we used the Database.saveResult to do the DML operation. What if I ask you to send an email to Admin for the failed Accounts there you can use the chain process to send the email to admins. Let us try for yourself as an assignment.

Limitations for queueable apex

let’s look at what are limitations for queueable apex.

  1. How many jobs can we schedule from queueable apex? Only one.
  2. Can I chain the jobs from test class? No, you cannot do that unless you want to end up with errors.
  3. How many jobs I can add by using system.enqueueJob for a single transaction? 50
  4. What is the stack depth for chaining the jobs? In developer edition it is limited to 5 including the parent job.
  5. Which one is easy to modify queueable or future? The answer is future as it resides in the apex class and easy to remove the annotation “@future”. Does it impact the test class no it won’t impact the test class? How cool it is right switching between synchronous to asynchronous and vice versa

Generic Approach to Salesforce Queueable

Future Vs queueable apex

Now let’s look at the main differences between Future and queueable apex

FUTURE APEXQUEUEABLE APEX
It is annotation based so we can use the same apex class to write the future method. Syntax: @futureIt is a class which implements’ queueable interface. Syntax: public class CLASS_NAME implements Queueable{}
We cannot monitor the jobsWe can monitor the jobs based on the job Id.
we cannot call a future from another future or batch apex. The limit on future method for single apex invocation is 50.We can chain the Queueable jobs and the stack depth in developer org is 5 and in enterprise edition you can chain 50 jobs.
Future method supports only primitive datatypesQueueable supports both primitive and non-primitive data types.
YouTube video

Conclusion

Which one to prefer for satisfying your logic? Based on this explanation I believe you will be in good shape to identify which one to choose.

Further Learning

Prakash Jada
Prakash Jada

5+ years of experience in all phases of SDLC (Analysis, Design, Development, Administration, Testing, Implementation, and Support) in software Applications using Salesforce (CRM). Well versed in Cloud Technology and on-premise infrastructure integration for Salesforce.com using Force.com platform, SOAP, REST web services, Lightning application, and third-party packages. Excellent interpersonal skills, and communication, accustomed to working in dynamic environments

Articles: 4

14 Comments

  1. Nice article…
    Only one thing I found future thing to remember point 3, there must be asynchronous..might be typo there.

    I Like Apex Hours alot thanks.

  2. There is small mistake. For future calls “It says We can only pass the Id’s as a parameter” which isn’t true. We can pass any primitive data type or collection of primitive data types as a parameter.

  3. Hi,
    Thanks for the wonderful and detailed info.
    It would be great if you could also add explanation about why non-premitive data types are allowed in Queueable apex? How actually the execution happens.
    Since Future and Queueable are both Async apex and in on case non-primitives are not allowyand in other it’s allowed. So there must be some reason for that.

  4. Hello,
    Thank you for the detail explanation!
    I may miss something, I don’t full understand the second example. I do see you use the State parameter, but where do you use the Contact parameter? also, you select the contact first and last name in the SOQL but i don’t see where you use it. The below code in the loop will create a new contact.
    Contact con = new Contact();
    con.AccountId = account.Id;
    updateContacts.add(con);

    How to link this new contact with the passed contact parameter? Do I miss something?

    Thank you,
    briant

Leave a Reply

Your email address will not be published. Required fields are marked *