26

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 6

18

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

4
  • 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. Commented Jan 22, 2014 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
    Commented Jan 24, 2014 at 19:23
  • 1
    It 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
    Commented Jul 24, 2014 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. Commented Jan 5, 2022 at 18:05
5

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
}
4
  • 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" Commented Jan 23, 2014 at 5: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. Commented Jan 24, 2014 at 21:07
  • pls see this link salesforce.stackexchange.com/questions/24935/… Commented Jan 27, 2014 at 9: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. Commented Jan 5, 2022 at 18:04
4

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.

2
  • 2
    this pattern has been updated in V3 of the book to use Queueables
    – cropredy
    Commented Apr 9, 2018 at 13:42
  • The video link you had shared is not showing in youtube , it says Video is unavailable and video is private
    – Hunt
    Commented Apr 16, 2020 at 6:50
3

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.

1
  • 1
    as of winter 19; UserInfo.getSessionId() will return a value for batchable transactions
    – cropredy
    Commented Jan 2, 2019 at 23:30
2

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

2
  • 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. Commented Jan 5, 2022 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
    Commented Mar 12 at 8:45
0

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:

https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platform_event_limits.htm

Edit: After I got mine working, I noticed this section in the Apex Developer Guide about implementing Database.RaisesPlatformEvents.

https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_batch_platformevents.htm

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?

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .