Advanced 25 min read

Future Methods

By the end of this lesson, you'll be able to:

  • Mark a method as asynchronous using @future
  • Explain the restrictions on a @future method's parameters
  • Recognize @future as the simplest, but most limited, async tool

Prerequisites: "Why Run Work Asynchronously?"

Marking a method as future

public class AccountSyncService {
    @future(callout=true)
    public static void syncAccountToExternalSystem(Id accountId) {
        Account acc = [SELECT Id, Name FROM Account WHERE Id = :accountId];

        HttpRequest request = new HttpRequest();
        request.setEndpoint('callout:External_CRM/accounts');
        request.setMethod('POST');
        request.setBody(JSON.serialize(acc));
        new Http().send(request);
    }
}
  1. public class AccountSyncService { — the class.
  2. @future(callout=true) — marks the method asynchronous; callout=true is required specifically because the method makes an HTTP callout.
  3. public static void syncAccountToExternalSystem(Id accountId) { — static, and an Id parameter, not the full record.
  4. Account acc = [SELECT Id, Name FROM Account WHERE Id = :accountId]; — fresh data, queried inside the method itself.
  5. (blank line)
  6. HttpRequest request = new HttpRequest(); — Module 36's request-building.
  7. request.setEndpoint('callout:External_CRM/accounts'); — a Named Credential endpoint.
  8. request.setMethod('POST'); — sending data, not just reading.
  9. request.setBody(JSON.serialize(acc)); — the Account serialized to JSON.
  10. new Http().send(request); — the callout, made possible here specifically because callout=true was set.
  11. } — closes the method.
  12. } — closes the class.

@future(callout=true) marks this method to run asynchronously — callout=true is required specifically because this method makes an HTTP callout (Module 36); without it, the callout would fail. Calling AccountSyncService.syncAccountToExternalSystem(acc.Id) from a trigger returns immediately, with the actual sync happening moments later in its own transaction.

The parameter restriction

// WRONG — @future methods cannot accept sObjects or objects as parameters
@future
public static void processAccount(Account acc) { ... } // compile error

// RIGHT — pass an Id, then query fresh data inside the method
@future
public static void processAccount(Id accountId) {
    Account acc = [SELECT Id, Name FROM Account WHERE Id = :accountId];
    // ...
}
  1. // WRONG — @future methods cannot accept sObjects or objects as parameters — framing the anti-pattern.
  2. @future — the annotation.
  3. public static void processAccount(Account acc) { ... } // compile error — passing a full Account record directly fails to compile.
  4. (blank line)
  5. // RIGHT — pass an Id, then query fresh data inside the method — framing the correct pattern.
  6. @future — the same annotation.
  7. public static void processAccount(Id accountId) { — a primitive Id parameter instead.
  8. Account acc = [SELECT Id, Name FROM Account WHERE Id = :accountId]; — fresh data, queried when the method actually runs.
  9. // ... — the rest of the method's logic.
  10. } — closes the method.

@future methods can only accept primitive types (Id, String, Integer, and collections of these) — never an sObject or a custom class directly. This is because the method's parameters get serialized and queued for later execution, and passing a full record risks it being stale by the time the method actually runs; passing just the Id and re-querying fresh data avoids that entirely.

The real limitations

@future methods must be static, cannot return a value (always void), and cannot call another @future method. These constraints are exactly why later lessons' tools (Queueable, Batchable) exist — they lift most of these restrictions, at the cost of slightly more setup.

Exercise

Write a @future method logAuditEvent(Id recordId, String eventType) that debugs both parameters.

Show hint

Use only primitive parameter types.

Salesforce: ○ Not connected Connect
APEX

Execution Results

Run your code to see the result.

Future Methods Quiz

1. Why can't a @future method accept an sObject directly as a parameter?

Log in to submit the quiz and save your score.

My Notes

Log in to keep private notes on this lesson.

Questions about this lesson

No questions yet — be the first to ask.

Log in to ask a question about this lesson.

Summary

@future is the simplest way to run a method asynchronously — a single annotation, with a few real restrictions that later lessons' tools don't share.