Skip to main content

SF-0356 · Scenario · Medium

Can we call future methods from a Trigger?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

Yes. Triggers are the most common caller of @future methods. Two scenarios usually motivate it:

  1. Callouts — triggers run inside a synchronous DML transaction, so they cannot call out directly. A @future(callout=true) runs after the trigger commits, in its own transaction where callouts are legal.
  2. Heavy work — long-running operations (huge calculations, big DML on related records) that would slow down the user’s save action.

The pattern

trigger ContactTrigger on Contact (after insert, after update) {
    Set<Id> contactIds = new Set<Id>();
    for (Contact c : Trigger.new) {
        if (c.Email != null && Trigger.isInsert) {
            contactIds.add(c.Id);
        }
    }
    if (!contactIds.isEmpty()) {
        ContactNotifier.notifyExternalSystem(new List<Id>(contactIds));
    }
}

public class ContactNotifier {
    @future(callout=true)
    public static void notifyExternalSystem(List<Id> contactIds) {
        List<Contact> cs = [SELECT Id, Email, FirstName FROM Contact WHERE Id IN :contactIds];
        // ... HTTP POST to external system
    }
}

Governor limit to respect

A single transaction can enqueue at most 50 future calls. If your trigger fires for a bulk insert of 500 records and you naively call @future once per record, you’ll hit:

System.LimitException: Too many future calls: 51

Always bulkify — pass a List<Id> to one future call, not 200 separate calls.

Recursive trigger guard still needed

Future methods run later, but they can still cause the same record’s trigger to fire again if the future does DML on it. Use a static flag if needed:

public class TriggerHandler {
    public static Boolean skipNext = false;
}

@future
public static void doWork(List<Id> ids) {
    TriggerHandler.skipNext = true; // ignored here — runs in new transaction!
    update [SELECT Id FROM Contact WHERE Id IN :ids];
}

Watch out: the static skipNext only matters within a single transaction. Since the future runs in a new transaction, you’d need a different guard (a custom setting flag, a check on a record field) to prevent recursion.

When future-from-trigger is the wrong tool

NeedBetter than @future?
Long-running batch on millions of recordsBatch Apex
Chained multi-step async logicQueueable
Real-time response back to the userLightning component with @AuraEnabled
Reliable retries on callout failurePlatform Event + subscriber, or Queueable with Finalizer

@future from a trigger is fine for fire-and-forget work. If you need to chain steps or report back, Queueable is the modern choice.

Common interview follow-ups

  • Can I call multiple future methods from one trigger? — Yes, up to 50 per transaction.
  • Does the future method see the trigger’s DML? — Yes. It runs after commit, so it sees the latest state.
  • Why not call out directly from the trigger?CalloutException: You have uncommitted work pending. The trigger transaction is still open.

Verified against: Apex Developer Guide — Future Methods. Last reviewed 2026-05-17.