[SalesForce] Calling a Callout via @future still gives uncommitted work pending error

Class A is simple page controller. It creates an instance of Class B.

The user can click a button on a VF page to invoke a pagereference method in Class A that will invoke various methods in class B, each of which make one or more callouts.
At the end of the of all the callouts, I invoke some final DML statements to avoid the uncommitted error.

But in one method in class B, I sometimes invoke a method in Class B marked with @future to make some additional callouts that can be processed async. Class B will then continue to make some more callouts via other methods in the current context.

But when I invoke the method marked as @future, I suddenly get an "uncommitted work pending" error for all subsequent callouts. But looking through the logs, there is no sign of any DML appearing before the callouts. Any ideas why calling a method marked with @future would cause the "uncommitted work pending" error?

EDIT:
Just created this example to demo the problem:

public class CalloutTester {

public Account a;

public CalloutTester() {
    a = new Account();
}

public void SubmitUpdates() {
    insert a;
}

public void Callout1() {


HttpRequest req = new HttpRequest();
  req.setEndpoint('http://www.thomas-bayer.com/sqlrest/CUSTOMER/18/');
  req.setMethod('GET');

    Http http = new Http();
    HTTPResponse res = http.send(req);
    res.getBody();
    a.name = 'Test';
    CalloutTester.Callout1F(); //Comment this out and it works
}

@future(callout=true)
public static void Callout1F() {


HttpRequest req = new HttpRequest();
  req.setEndpoint('http://www.thomas-bayer.com/sqlrest/CUSTOMER/18/');
  req.setMethod('GET');

    Http http = new Http();
    HTTPResponse res = http.send(req);
    res.getBody();

}



public void Callout2() {


HttpRequest req = new HttpRequest();
  req.setEndpoint('http://www.thomas-bayer.com/sqlrest/CUSTOMER/18/');
  req.setMethod('GET');

    Http http = new Http();
    HTTPResponse res = http.send(req);
    res.getBody();
    a.name = 'Test2';
}


}

If you invoke this code in the Anon Console, you'll get an error – but comment out the line calling the @future, and it works fine

CalloutTester ct = new CalloutTester();
    ct.Callout1();
    ct.Callout2();
    ct.SubmitUpdates();

Best Answer

Making a call to a @future method results in an AsyncApexJob record being created with the JobType of 'Future'. While the DML isn't explicit, a record is being created that would otherwise need to be rolled back in the event of an exception.

If something goes wrong with the subsequent callouts that queued future method call will need to be rolled back. But, then you have the problem of not being able to rollback any successful callouts that occured before the rollback was initiated.

Related Topic