Newer Version Available

This content describes an older version of this product. View Latest

Future Methods

A future method runs asynchronously. You can call a future method to run long-running operations, such as callouts to external web services or any operation that you want to run in its own thread. You can also use future methods to isolate Data Manipulation Language (DML) operations on different sObject types to prevent the mixed DML error. Each future method is queued and runs when system resources become available. That way, the execution of your code doesn’t wait for the completion of a long-running operation. A benefit of future methods is that some governor limits are higher, such as SOQL query limits and heap size limits.

Salesforce now recommends that you use Queueable Apex instead of Apex future methods. Queueables have the same use cases as future methods but offer more benefits, including job IDs, support for non-primitive types, and job chaining.

See Queueable Apex.

Important

To define a future method, annotate it with the Future annotation.

1public with sharing class FutureClass {
2    @Future
3    public static void myFutureMethod()
4    {   
5         // Perform some operations
6    }
7}

Methods with the Future annotation must be static methods, and can only return a void type. The specified parameters must be primitive data types, arrays of primitive data types, or collections of primitive data types. Methods with the Future annotation can’t take sObjects or objects as arguments.

The reason why sObjects can’t be passed as arguments to future methods is because the sObject can change between the time that you call the method and the time that it executes. In this case, the future method gets the old sObject values and can overwrite them. To work with sObjects that already exist in the database, pass the sObject ID or the collection of IDs instead. Then use the ID to perform a query for the most up-to-date record. This example shows how to do so with a list of IDs.

1public with sharing class FutureMethodRecordProcessing {
2    @Future
3    public static void processRecords(List<ID> recordIds)
4    {   
5         // Get those records based on the IDs
6         List<Account> accts = [SELECT Name FROM Account WHERE Id IN :recordIds WITH USER_MODE];
7         // Process records
8    }
9}

Here’s 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. To learn more about callouts, see Invoking Callouts Using Apex.

1public with sharing class FutureMethodExample {
2    @Future(callout=true)
3    public static void getStockQuotes(String acctName)
4    {   
5         // Perform a callout to an external service
6    }
7
8}

Insert a user with a non-null role in a separate thread from DML operations on other sObjects. In this example, the future method, insertUserWithRole, which is defined in the Util class, performs the insertion of a user with the COO role. This future method requires the COO role to be defined in the org. The useFutureMethod method in MixedDMLFuture inserts an account and calls the future method insertUserWithRole.

This Util class contains the future method for inserting a user with a non-null role.

1public with sharing class Util {
2    @Future
3    public static void insertUserWithRole(
4        String uname, String al, String em, String lname) {
5
6        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User' WITH USER_MODE];
7        UserRole r = [SELECT Id FROM UserRole WHERE Name='COO' WITH USER_MODE];
8        // Create new user with a non-null user role ID 
9        User newUser = new User(alias = al, email=em, 
10            emailencodingkey='UTF-8', lastname=lname, 
11            languagelocalekey='en_US', 
12            localesidkey='en_US', profileid = p.Id, userroleid = r.Id,
13            timezonesidkey='America/Los_Angeles', 
14            username=uname);
15        insert as user newUser;
16    }
17}

This class contains the main method that calls the future method.

1public with sharing class MixedDMLFuture {
2    public static void useFutureMethod() {
3        // First DML operation
4        Account a = new Account(Name='Acme');
5        insert as user a;
6        
7        // This next operation (insert a user with a role) 
8        // can't be mixed with the previous insert unless 
9        // it is within a future method. 
10        // Call future method to insert a user with a role.
11        Util.insertUserWithRole(
12            'mruiz@awcomputing.com', 'mruiz', 
13            'mruiz@awcomputing.com', 'Ruiz');        
14    }
15}

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

Future Method Limits

Methods with the Future annotation have these limits.

  • No more than 0 in batch and future contexts; 50 in queueable context method calls per Apex invocation. Asynchronous calls, such as Future or executeBatch, that are called in a startTest or stopTest block don’t count against your limits for the number of queued jobs.

    Having multiple future methods fan out from a queueable job isn’t a recommended practice as it can rapidly add many future methods to the asynchronous queue. Request processing can be delayed and you can quickly hit the daily maximum limit for asynchronous Apex method executions. See Future Method Performance Best Practices and Lightning Platform Apex Limits.

    Note

  • The maximum number of Future method invocations per a 24-hour period is 250,000 or the number of user licenses in your organization multiplied by 200, whichever is greater. This limit is for your entire org and is shared with all asynchronous Apex: Batch Apex, Queueable Apex, scheduled Apex, and future methods. To check how many asynchronous Apex executions are available, make a request to REST API limits resource. See List Organization Limits in the REST API Developer Guide. If the number of asynchronous Apex executions needed by a job exceeds the available number that’s calculated by using the 24-hour rolling limit, an exception is thrown. For example, if your async job requires 10,000 method executions and the available 24-hour rolling limit is 9,500, you get the AsyncApexExecutions Limit exceeded exception. The license types that count toward this limit include full Salesforce and Salesforce Platform user licenses, App Subscription user licenses, Chatter Only users, Identity users, and Company Communities users.
  • Future jobs queued by a transaction aren’t processed if the transaction rolls back.
  • Future method jobs queued before a Salesforce service maintenance downtime remain in the queue. After service downtime ends and when system resources become available, the queued future method jobs are executed. If a future method was running when downtime occurred, the future method execution is rolled back and restarted after the service comes back up.

Note

Testing Future Methods

To test methods defined with the Future annotation, call the class containing the method in a startTest(), stopTest() code block. All asynchronous calls made after the startTest method are collected by the system. When stopTest is executed, all asynchronous processes are run synchronously.

For our example, here’s the test class.
1@IsTest
2private class MixedDMLFutureTest {
3    @IsTest static void test1() {
4        User thisUser = [SELECT Id FROM User WHERE Id = :UserInfo.getUserId() WITH USER_MODE];
5       // System.runAs() allows mixed DML operations in test context
6        System.runAs(thisUser) {
7            // startTest/stopTest block to run future method synchronously
8            Test.startTest();        
9            MixedDMLFuture.useFutureMethod();
10            Test.stopTest();
11        }
12        // The future method will run after Test.stopTest();
13    
14        // Verify account is inserted
15        Account[] accts = [SELECT Id from Account WHERE Name='Acme' WITH USER_MODE];
16        Assert.areEqual(1, accts.size());
17        // Verify user is inserted
18        List<User> users = [SELECT Id from User WHERE username='mruiz@awcomputing.com' WITH USER_MODE];
19        Assert.areEqual(1, users.size());
20    }
21}

Future Method Performance Best Practices

Salesforce uses a queue-based framework to handle asynchronous processes from such sources as future methods and batch Apex. This queue is used to balance request workload across organizations.To ensure that your organization is efficiently using the queue for your asynchronous processes:

  • Avoid adding large numbers of future methods to the asynchronous queue, if possible. If more than 2,000 unprocessed requests from a single organization are in the queue, any additional requests from the same organization will be delayed while the queue handles requests from other organizations.
  • Make sure that future methods run as fast as possible. To ensure fast execution of batch jobs, minimize web service callout times and tune queries used in your future methods. The longerthe future method runs, the more likely other queued requests are delayed when there are many requests in the queue.
  • Test your future methods at scale. To help determine if delays can occur, test by using an environment that generates the maximum number of future methods that you expect to handle.
  • Consider using batch Apex instead of future methods to process large numbers of records.