[SalesForce] Command Button Invoke Action After Async JavaScript

I have a page that prompts the user for information and may need to do multiple web service callouts. Each callout needs to do DML when complete, so I'm using Visualforce Remoting to perform those actions first. Once the actions are complete, I need to execute an Apex method to do some more work. This may be more of a JavaScript question, but hopefully someone has the answer.

Right now, the page doesn't wait for all the remoting calls to finish before invoking the action. How can I get it to wait?

My Button:

<apex:commandButton action="{!buildFax}" onclick="checkForCharts(function(){return true});" value="Build Fax" />

My JS (somewhat simplified):

function getChart(client, accession, callback) { 
        PatientCharts.getPatientChartToSObjectRemote(
        '{!theCase.Id}',
        client,
        accession,
        function(result, event){
            callback(event);
        });
    }

    function checkForCharts(docallback) {
        // Async worker counters
        var workers = 0;

        function tickWorkers() {
            workers--;
            if ( workers === 0 ) {
                if (docallback)
                    docallback();
            }
        }

        j$('[id$="patTab"]')
            .find('input[type="checkbox"][name$="chart"]:checked').closest('tr')
            .each(function() {
                var client = j$(this).find('[id$="client"]').text();
                var accession = j$(this).find('[id$="accn"]').text();
                workers++;
                getChart(client, accession, function(eventres) {
                    tickWorkers();
                });

            })

Best Answer

The answer is apex:actionFunction to generate a JavaScript function bound to an APEX method that can be passed as a JavaScript callback.

Let's try to understand, what happens when pressing your button:

<apex:commandButton action="{!buildFax}" onclick="checkForCharts(function(){return true});" value="Build Fax" />

Of course, the onclick code is executed first. It means invoking checkForCharts, that correctly starts the asynchronous workers and returns no value. Then the returned value (in our case nothing, or rather the undefined constant) is checked:

  • if it's identical to false, no further actions are executed,
  • otherwise the action method is called normally.

Thus the method buildFax is called immediately after setting up workers, not waiting for evaluation of the callback.

After this long introduction, the fix is easy:

<apex:actionFunction action="{!buildFax}" name="doBuildFax" />
<apex:commandButton value="Build Fax" onclick="checkForCharts(doBuildFax); return false;" />
Related Topic