0

I'm trying to call showData() after makeCallout() finish executing. makeCallout() has insert data; so I'm getting this error:

Content cannot be displayed: You have uncommitted work pending. Please commit or rollback before calling out.

So I created a new method that has @future anotation. since according to Salesforce

Each future method is queued and executes when system resources become available.

What I'm trying to achieve is running showData() after makeCallout () finishes, I trying the below but the variables in showData() are null since both methods are executed at the same time.

public class myClass {
.....

private static integer a = 0 ;
public void makeCallout {

Http http = new Http();
HttpRequest request = new HttpRequest();
Transient string cookie = 'X';
request.setEndpoint(url);
request.setMethod('POST');       
request.setHeader('Authorization', authheader);
request.setHeader('Cookie', cookie);
if (response.getStatusCode() == 200) {
    while (parser.nextToken() != null){
        if (parser.getCurrentToken() == JSONToken.START_ARRAY) {
            if (parser.getCurrentToken() == JSONToken.START_OBJECT){
                try {
                    a = parser.getText();
                } 
            } 
        } 
    }
    insert a;
    showData();
} 

@future
public static void showData(){
    //make another HTTP call
    insert newData
    System.Debug('a = ' + a);  // a=0 
}
2
  • So, are you doing a callout then? Your post would be easier to address if you use a more situation specific example.
    – Adrian Larson
    Commented Jul 28, 2017 at 16:17
  • @AdrianLarson please see the updated example, I'm making 2 callouts and 2 DMLs with data being called from VF page
    – Has Nah
    Commented Jul 28, 2017 at 16:42

2 Answers 2

0

Future methods are called in different context/transaction. a is 0 not because they're being called at the same time, but actually because they're NOT being called at the same time. If possible, save your DML until after all the callouts are done. If that's not viable, then you'll have to callout, DML, and then chain to a second transaction. In Visualforce, you could do something like this:

<apex:actionFunction name="method1" action="{!method1}" reRender="" oncomplete="method2()" />
<apex:actionFunction name="method2" action="{!method2}" reRender="" />

Here, the client chains from method1 to method2 for you, causing two separate transactions. Even better, you can display the results to the user if you like, since when method2 is done, everything has completed. Please note that your variables (e.g. a) must not be static, so that they can be transferred as part of the view state.

1
  • I ended up refactoring the code and making callouts before DML statements, which also decrease viewstate size.
    – Has Nah
    Commented Aug 9, 2017 at 2:32
1

Take a look at the Apex Developer Guide:

Performing DML Operations and Mock Callouts

By default, callouts aren’t allowed after DML operations in the same transaction because DML operations result in pending uncommitted work that prevents callouts from executing. Sometimes, you might want to insert test data in your test method using DML before making a callout. To enable this, enclose the portion of your code that performs the callout within Test.startTest and Test.stopTest statements. The Test.startTest statement must appear before the Test.setMock statement. Also, the calls to DML operations must not be part of the Test.startTest/Test.stopTest block.

DML operations that occur after mock callouts are allowed and don’t require any changes in test methods.

The DML operations support works for all implementations of mock callouts using: the HttpCalloutMock interface and static resources (StaticResourceCalloutMock or MultiStaticResourceCalloutMock). The following example uses an implemented HttpCalloutMock interface but you can apply the same technique when using static resources.

You can either wrap your callout in between Test.startTest()/Test.stopTest() calls, or make it asynchronously. Your callout has to be in a separate transaction. I suppose you could also use system.runAs(User), though that wouldn't usually make a ton of sense in a test context.

You must log in to answer this question.

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