Salesforce Developer Javascript Remoting Example4

salesforce-developer-javascript-remoting

https://forceadventure.wordpress.com/2013/07/22/concurrency-javascript-remoting-salesforce/

// Salesforce - Developer - JavaScript Remoting - Example 4 - Concurrency Issue:

The asynchronous nature of javascript remoting has the potential to be a 
negative. Here, we’ll see an example of how you might get unexpected results 
because of that. A hat tip goes out to Dan Appleman whose method of reproducing 
concurrency issues in salesforce was blatantly stolen for this post.

Consider the following Visualforce page:

<apex:page standardController="Account" 
  extensions="JSRemotingConcurrencyController" id="page">

  <script type="text/javascript">

// Method to get the Counter of the Account bound to the page by the 
// standardController
// Sends a parameter of the Account Id to the controller    
    function getAcctCounter() {
      var text = "";
      JSRemotingConcurrencyController.doGetAcctCounter(
        '{!Account.Id}',
        function(result, event) {
          if(event.type === 'exception') {
            console.log("exception");
            console.log(event);
          } else if(event.status) {
            text = result;
            document.getElementById('acctCounterResponse').innerHTML = text;
          } else {
            console.log(event.message);
          }
        }
      );
    }

// Method to increment the Counter of the Account bound to the page by the 
// standardController
// Sends a parameter of the Account Id to the controller    
    function incrementAccountCounter(bufferBool) { 
      var max = document.getElementById('incrementCounter').value;
      for(i=0; i<max; i++) {
        JSRemotingConcurrencyController.doIncrementValue(
          '{!Account.Id}', 
          function(result, event) {
            if(event.type === 'exception') {
              console.log("exception");
              console.log(event);
            } else if (event.status) {
              console.log(result);
            } else {
              console.log(event.message);
            }
          },
          {buffer: bufferBool}  //This is really important!
        );
      }
    }

  </script>

  <h2>Concurrency Concerns with Javascript Remoting </h2> <br /> <br />

  <input type="number" id="incrementCounter"> </input> <br />
  <button onclick="incrementAccountCounter(true)">
    Increment Counter with Buffer</button>
  <button onclick="incrementAccountCounter(false)">
    Increment Counter without Buffer</button> <br /> <br />

  <button onclick="getAcctCounter()">Get Account Counter</button> <br/>
  <span id="acctCounterResponse"></span>
</apex:page>

and the following controller:

global with sharing class JSRemotingConcurrencyController {
    public JSRemotingConcurrencyController(ApexPages.StandardController c) {
    }

    @RemoteAction
    public static String doGetAcctCounter(String acctId) {
        if(acctId != null) {
            Account acct = getAccount(acctId);
            if(acct != null) {
                return String.ValueOf(acct.Counter__c);
            } else {
                return 'Account could not be found';
            }
        } else {
            return 'Account Id was null';
        }
    }

    @RemoteAction
    public static String doIncrementValue(String acctId) {
        if(acctId != null) {
            Account acct = getAccount(acctId);
            if(acct != null) {
                acct.Counter__c+=1;
                update acct;
                return String.valueOf(acct.Counter__c);
            } else {
                return 'Account could not be found';
            }
            return getAccount(acctId).Name;
        } else {
            return 'Account Id was null';
        }
    }

    private static Account getAccount(String acctId) {
        list<Account> accounts = [SELECT Id, Name, Counter__c
                                  FROM Account
                                  WHERE Id =: acctId];
        if(accounts.isEmpty()) {
            return null;
        } else {
            return accounts[0];
        }
    }
}

What we have here is a numeric input to determine how many times to call our 
increment method and two buttons. They both call the incrementAccountCounter 
javascript method. One of them sets bufferBool to true, the other sets 
bufferBool to false. Below that, theres a button that gets the current value 
of Counter__c of the Account associated with this page.

When this page is viewed (make sure to use ?id=[account_Id] after the page name 
in the URL), it should look like the following. Here’s the initial state of our 
Counter on this account. We can see that the counter is at 1.

This is where we see how important the buffer setting is. We’ll run our 
increment method 10 times with buffering set to true by setting the numeric 
input to 10 and clicking the “Increment Counter with Buffer” button. After 
execution, the counter has incremented to 11 as would be expected.

Now let’s see how turning buffering off affects things. We’ll run our increment 
method 10 times again but this time with buffering set to false by clicking the 
“Increment Counter without Buffering” button. After execution, the counter has 
NOT incremented to 21 as might be expected.

Instead of incrementing by 10, we can see that the counter only incremented by 
3. This is because, when buffer is set to false, multiple SELECT statements run 
at the same time and they concurrently update the same value. In this case, the 
calls are asynchronous and don’t wait for previous calls to finish before 
executing.

During testing, (and I tested a lot) I saw incrementing values of between 2 
and 7 when running 10 unbuffered calls. I was never able to get the values to 
increment incorrectly with buffering on.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License