Sync and async contexts have different limit ceilings — async generally gets more headroom because it's not blocking a user.
| Limit | Sync | Async | |---|---|---| | SOQL queries | 100 | 200 | | DML statements | 150 | 150 | | Heap size | 6 MB | 12 MB | | CPU time | 10,000 ms | 60,000 ms | | Future calls / queueable enqueues per transaction | 50 | 50 |
Async contexts (@future, Queueable, Batch, Schedulable) get:
- More CPU time — useful for long-running calculations.
- More heap — can load larger record sets.
- More SOQL — query more data per transaction.
But DML is still capped at 150 in async — async doesn't help with bulk-write needs.
Important nuances:
- Batch Apex execute() runs once per chunk. Each chunk is its own transaction with its own limits. So a Batch processing 10M records in chunks of 200 effectively gets 200×its-limits-per-execute = lots of total work, but each execute is bounded.
- Queueable can chain — fire another Queueable from one Queueable to extend processing. Chaining is unlimited but each link has its own per-transaction limits.
- `@future` cannot enqueue another `@future` — Salesforce blocks the chain to prevent infinite recursion.
Don't try to fit a 1M-record update into one async run. Use Batch Apex with 200 records per chunk — that's the bulk-friendly pattern.
