I have a requirement where a future method need to be called from batch apex ,I came across various articles stating that a future method could not be called from the batch. Is there any alternate solutions for that?
6 Answers
UPDATED ANSWER
(not batch but...).
I was investigating the ScheduledDispatcher: https://gist.github.com/gbutt/11151983
And lo and behold this works:
global class ScheduledDispatcher Implements Schedulable{
public Interface IScheduleDispached{
void execute(SchedulableContext sc);
}
global void execute(SchedulableContext sc){
Type targetType = Type.forName('{HANDLERNAME');
if(targetType != null){
IScheduleDispached obj = (IScheduleDispached)targetType.newInstance();
obj.execute(sc);
}
}
}
public class {HANDLERNAME} implements ScheduledDispatcher.IScheduleDispached {
public void execute(SchedulableContext sc)
{
//Call your Future Method Here
}
}
If you still need to do it from within the batch context you can do as previously suggested:
public static method1(){
method2();
}
@future
public static method2(){
}
call method1 from the batch and method2 from elsewhere
- 54,152
- 11
- 100
- 195
-
This is a major limitation because we may not directly be calling the class from the Batch class, but a trigger may fire which would call a future method. – Sagar Pareek Jan 22 '14 at 13:15
-
Which is why you need to check for that condition. Also, during the batch, any future methods that need to be ran should be directly called – Eric Jan 24 '14 at 19:23
-
1It sounds like he needs the functionality from that trigger (including the asynchronous part), for example - if he is doing anything with Setup objects and Non-Setup objects he will not be able to make the call synchronously. – Chris Jul 24 '14 at 13:16
-
I'm facing this very problem. Where I need to update the order records through batch. And then I have a trigger on order which needs to fire and that trigger is making call to future method and that future method making call-out for every single order record. As everything is happening in a single transaction. So I'm getting the async exception and the future method can't be called from batch. – LetMeCodeYou Jan 05 '22 at 18:05
This is a problem: Future methods cannot be called from Batch processes. In my experience, it's best to test for this before running triggered future methods:
public void runContactUpdates(list<Contact> triggernew)
{
if (System.isFuture() || System.isBatch())
runContactUpdatesNow(triggernew);
else
runContactUpdatesFuture((new map<Id, Contact>(triggernew)).keyset());
}
@Future
public void runContactUpdatesFuture(set<Id> conids)
{
runContactUpdatesNow([select Id, Name from Contact where Id in :conids]);
}
public void runContactUpdatesNow(list<Contact> triggernew)
{
//do updates to contacts. Maybe update to database
}
- 5,419
- 24
- 37
-
My real scenario is Im using batch process to collect a bunch of accounts and i have scheduled that batch with batch size 1.Then I need future callout to send emails for the contacts of the account asynchronously.When i try this without @future im getting error like "you have uncommited works" – Manoj Chandran Jan 23 '14 at 05:52
-
I don't understand why you would need @future to just send emails. If you post your code, we may be able to help. – Jeremy Nottingham Jan 24 '14 at 21:07
-
pls see this link http://salesforce.stackexchange.com/questions/24935/processing-callouts-from-batch – Manoj Chandran Jan 27 '14 at 09:19
-
I'm facing this very problem. Where I need to update the order records through batch. And then I have a trigger on order which needs to fire and that trigger is making call to future method and that future method making call-out for every single order record. As everything is happening in a single transaction. So I'm getting the async exception and the future method can't be called from batch. – LetMeCodeYou Jan 05 '22 at 18:04
The general solution to this and other async problems (i.e. unlimited future calls) was proposed with code samples by Dan Appleman at Dreamforce 13.
The code can be found at http://advancedapex.com/dreamforce13/
The essence of the solution is to use a custom object to store all async requests and then have a scheduled class launch a batch job to process each async request one-by-one, restarting the scheduled job in the finish() method if there are new async requests created since the batch was started.
- 71,240
- 8
- 120
- 270
-
2
-
The video link you had shared is not showing in youtube , it says
Video is unavailable and video is private– Hunt Apr 16 '20 at 06:50
In case anyone is looking for a better solution, I may have one. While @Future cannot be called from a batch class, a webservice can. A webservice can also call an @future method. So have your batch class call an apex webservice that in turn calls your @future method.
There is one trick here. To call your webservice from the batch class, you need a session Id. Don't call Userinfo.getsessionid from a scheduled batch class. You won't get a result. Instead, call Userinfo.getsessionId from your scheduleable class and pass the id into your batch class as a parameter. Now you have everything you need to call @Future from a batch.
- 81
- 3
-
1as of winter 19; UserInfo.getSessionId() will return a value for batchable transactions – cropredy Jan 02 '19 at 23:30
A future method can be invoked from the Finish method of batch apex, of course it will not allow from the Execute method. I did test it for one of my requirements and future call is invoking from the Finish method with out any issues
- 21
- 1
-
I'm facing this very problem. Where I need to update the order records through batch. And then I have a trigger on order which needs to fire and that trigger is making call to future method and that future method making call-out for every single order record. As everything is happening in a single transaction. So I'm getting the async exception and the future method can't be called from batch. – LetMeCodeYou Jan 05 '22 at 18:03
-
Running it from the finish method didn't work for me, still threw an error. Maybe things have changed in the last 9 years :-) – MikeA Mar 12 '24 at 08:45
Always keep a Platform Event in your toolbox!
To work around running a Future method from batch, you can instead fire a Platform Event, and then run the Future method from a Trigger on the Platform Event.
E.g. you want to set Opps to won in batch, but existing automation runs a Future method when an Opp is won. Create a Platform Event and add a custom text field for OpportunityId. In your batch, create a Platform Event for each Opp to be won, populating the OpportunityId field, and add to a list of Platform Events. Publish the list of Platform Events.
List<UpDownSell_Opp_AutoCreated__e> oppsToWinEvents = new List<UpDownSell_Opp_AutoCreated__e>();
for (Id oppId : oppIdsList) {
oppsToWinEvents.add(new UpDownSell_Opp_AutoCreated__e(OpportunityId__c = oppId));
}
List<Database.SaveResult> results = EventBus.publish(oppsToWinEvents);
In the Platform Event Trigger, iterate over trigger.new, get the Opp Ids and set to won from there.
trigger UpDownSell_Opp_AutoCreated on UpDownSell_Opp_AutoCreated__e (after insert) {
List<Opportunity> oppsToWin = new List<Opportunity>();
for (UpDownSell_Opp_AutoCreated__e event : Trigger.New) {
oppsToWin.add(new Opportunity(Id = event.OpportunityId__c, StageName='5-Won'));
}
update oppsToWin;
}
The Opps are set to won and the existing Future method runs successfully.
Keep limits in mind as documented here, along with other Platform Event development information:
Edit: After I got mine working, I noticed this section in the Apex Developer Guide about implementing Database.RaisesPlatformEvents.
public with sharing class YourSampleBatchJob implements Database.Batchable<SObject>,
Database.RaisesPlatformEvents{
// class implementation
}
Mine works without it, which is in agreement with this post, so not needed in this case.
When is Database.RaisesPlatformEvents needed on a Database.Batchable?
- 1,075
- 11
- 28