Why This Is Useful
Your trigger is slow. You’ve already bulkified it, you’ve pulled the SOQL out of the loops, and it’s still dragging. Users are sitting there watching a spinner every time they save a record.
The thing I see most often in slow triggers is an enormous amount of logic that doesn’t need to be there at all, because the user doesn’t need to see the result immediately.
And that’s the trick: if the user doesn’t need to see it right now, it doesn’t belong in your trigger. Hand it off to a Queueable job and let it happen a minute later. Nobody will notice, and the save gets its time back.
The Code
Create a class that implements Queueable:
public class AccountProcessorQueueable implements Queueable
{
private List<Account> accounts;
public AccountProcessorQueueable(List<Account> accountsToProcess)
{
this.accounts = accountsToProcess;
}
public void execute(QueueableContext context)
{
//All the slow stuff that nobody is waiting on
for (Account acct : accounts)
{
acct.Description = 'Processed asynchronously';
}
update accounts;
}
}Two things: implement the interface, and take your data in through the constructor so execute() has something to work with.
Then enqueue it:
System.enqueueJob(new AccountProcessorQueueable(Trigger.new));That’s it. The save completes immediately, the heavy work happens in its own transaction shortly after.
Please Don’t Put That Line In The Trigger
I showed it that way because it’s the shortest possible demonstration. In real code that enqueueJob call belongs in a trigger handler or a domain class, not in the trigger file itself.
Your trigger should stay one line. Same rule as always.
The Bonus You Get For Free
Going async doesn’t just make the save feel faster, it also roughly doubles your governor limits. 200 SOQL queries instead of 100. 12MB of heap instead of 6MB. 60 seconds of CPU instead of 10.
So if your trigger is bumping into limits rather than just feeling sluggish, this can solve that too.
You can also make callouts from a Queueable, which you flat out cannot do after DML in a synchronous trigger. Add Database.AllowsCallouts to the class and you’re away.
What To Watch For
Errors go quiet. There’s no user watching, so a failure just… doesn’t happen visibly. Log your failures somewhere you’ll actually look.
You get 50 queued jobs per transaction. Don’t enqueue one per record, enqueue one job with all the records.
Tests need Test.startTest() and Test.stopTest(). The async work only runs at stopTest(). Forget the wrapper and your test passes without testing anything, which is a fun way to get a false sense of security.
More detail in my complete guide to asynchronous Apex.
Get Coding With The Force Merch!!
We now have a redbubble store setup so you can buy cool Coding With The Force merchandise! Please check it out! Every purchase goes to supporting the blog and YouTube channel.
Get Shirts Here!
Get Cups, Artwork, Coffee Cups, Bags, Masks and more here!
Check Out More Coding With The Force Stuff!
If you liked this post make sure to follow us on all our social media outlets to stay as up to date as possible with everything!
Youtube
Patreon
Github
Facebook
Twitter
Instagram
Salesforce Development Books I Recommend
Advanced Apex Programming
Salesforce Lightning Platform Enterprise Architecture
Mastering Salesforce DevOps
Good Non-SF Specific Development Books:
Clean Code
Clean Architecture