100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Future Methods and Async Apex

Learn how @future methods let Apex defer work to a separate asynchronous transaction with its own governor limits, and when to reach for them over other async tools.

Advanced ApexIntermediate8 min readJul 10, 2026
Analogies

Understanding Asynchronous Apex

Asynchronous Apex lets you defer processing to a separate transaction that runs later, outside the current request's context. This matters because Salesforce enforces strict per-transaction governor limits — such as 100 SOQL queries or 150 DML statements — and synchronous code that calls out to external systems or processes large data volumes can easily exceed them. By pushing work onto a new thread with its own fresh set of limits, asynchronous Apex — future methods, queueable Apex, batch Apex, and scheduled Apex — lets you handle heavier workloads without blocking the user or breaching limits.

🏏

Cricket analogy: It's like a captain calling for a drinks break mid-innings in a Test match — the over in progress pauses and resumes with fresh legs, rather than forcing the bowler to complete a spell that would exhaust him within the same session.

The @future Annotation

A future method is a static, void-returning Apex method annotated with @future that executes asynchronously when the Salesforce servers free up resources, typically within minutes but not on a guaranteed schedule. Its parameters must be primitive types, primitive collections, or arrays of primitives — never sObjects or objects — because the call is serialized and queued, so passing a full Account record isn't allowed; instead you pass a Set<Id> of record Ids and requery the data inside the method. If the method needs to make an HTTP callout to an external system, you must add callout=true to the annotation, since regular synchronous Apex triggered by a DML operation cannot make callouts directly.

🏏

Cricket analogy: It's like passing a scorer only the jersey number of a batsman rather than his full profile — the future method rebuilds the detail itself, in the same way it re-queries a record from just the Id you handed it.

apex
public class AccountFutureUtil {
    @future(callout=true)
    public static void updateAccountRating(Set<Id> accountIds) {
        List<Account> accountsToUpdate = new List<Account>();
        for (Account acc : [SELECT Id, AnnualRevenue FROM Account WHERE Id IN :accountIds]) {
            HttpRequest req = new HttpRequest();
            req.setEndpoint('callout:CreditRatingService/accounts/' + acc.Id);
            req.setMethod('GET');
            HttpResponse res = new Http().send(req);
            if (res.getStatusCode() == 200) {
                acc.Rating = res.getBody();
                accountsToUpdate.add(acc);
            }
        }
        update accountsToUpdate;
    }
}

Limitations and Best Practices

Future methods come with hard constraints: you cannot call one future method from another, they cannot return values or accept sObjects/objects as arguments, and Salesforce guarantees neither the order nor the exact timing of execution — two future calls queued together may finish in reverse order. A single transaction can enqueue at most 50 future method calls, and because they run outside your original context, you lose the ability to roll them into the same commit as the triggering DML. In unit tests, code inside Test.startTest() and Test.stopTest() forces all queued future calls to execute synchronously at stopTest(), which is essential for asserting on their side effects.

🏏

Cricket analogy: It's like a franchise being capped at signing only a fixed number of overseas players per season — a hard roster limit, similar to the 50 future calls allowed per transaction.

Never invoke a future method from a trigger that might itself run inside another asynchronous context (like from within a batch Apex execute method) — Apex will throw a 'Future method cannot be called from a future or batch method' runtime exception.

When to Use Future Methods vs Other Async Options

Future methods are the simplest tool for fire-and-forget work like a single callout after a record save, but they're increasingly superseded by Queueable Apex, which supports chaining, sObject parameters, and job monitoring via the AsyncApexJob object. Choose future methods for quick, isolated tasks such as recalculating a related record's rollup after an update; choose Batch Apex when you must process more than a handful of records against governor limits per chunk; and choose Scheduled Apex when the work needs to run on a recurring cron-like cadence rather than being triggered by user action.

🏏

Cricket analogy: It's like choosing a pinch hitter for a quick six-ball cameo rather than sending in your anchor batsman built for a long, patient partnership — the right tool depends on the length of the job.

You can monitor any asynchronous Apex job, including future method invocations, by querying the AsyncApexJob object — check the Status field for values like Queued, Processing, Completed, or Failed.

  • @future methods run asynchronously on a static, void method and cannot be called from other future or batch contexts.
  • Parameters must be primitives, primitive collections, or arrays of primitives — never sObjects or objects.
  • Use callout=true on the annotation to allow the future method to make HTTP callouts.
  • A single transaction can enqueue at most 50 future calls, and execution order/timing is never guaranteed.
  • Test.startTest()/Test.stopTest() forces queued future methods to run synchronously for testing.
  • Queueable Apex is generally preferred over future methods for new development since it supports sObjects, chaining, and job monitoring.
  • Query AsyncApexJob to monitor the status of any asynchronous Apex execution, including future methods.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#ApexSalesforceStudyNotes#FutureMethodsAndAsyncApex#Future#Methods#Async#Apex#Functions#Concurrency#StudyNotes#SkillVeris