Search code examples
javascriptajaxasynchronoussalesforceremoting

Function/Event when multiple javascript remoting functions complete


I am working on an HTML5 mobile web app built on top of SalesForce. We make heavy use of JavaScript Remoting functions and I would like to know a way to wait for N (multiple) of these remote function calls to complete and then fire another event or function call. The use case we are trying to accomplish is as follows:

  1. Show jQuery Spinner/Loader with $.mobile.showPageLoadingMsg();
  2. Call Remote function 1, 2, 3, ..., N.
  3. Wait for Remote functions 1, 2, 3, ..., N to return but do NOT freeze the browser.
  4. Hide the jQuery Spinner/Loader with $.mobile.hidePageLoadingMsg();

    // Show a Loading Spinner while we wait for our remote requests to complete.
    $.mobile.showPageLoadingMsg();
    
    // Remote Request 1
    MyController.f1(
        function(result, event){
            if(event.type == 'exception') {
                alert('Error: ' + event.message);                     
            } else {
                // Do stuff here such as fill in a form with the results from f1
            }
        },
        {escape:true});
    
    // Remote Request 2
    MyController.f2(
        function(result, event){
            if(event.type == 'exception') {
                alert('Error: ' + event.message);                     
            } else {
                // Do stuff here such as fill in a form with the results from f2
            }
        },
        {escape:true});
    
    // ...
    
    // Remote Request N
    MyController.fN(
        function(result, event){
            if(event.type == 'exception') {
                alert('Error: ' + event.message);                     
            } else {
                // Do stuff here such as fill in a form with the results from fN
            }
        },
        {escape:true});
    
    // I want to wait until all of my requests to finish before I hide the loading spinner.
    wait_for_all(); 
    $.mobile.hidePageLoadingMsg();  
    

Solution

  • here is how I generally handle this case:

        function doManyAsync(doneCallback) {
            // Async worker counters
            var workers = 0;
    
            function tickWorkers() {
                workers--;
                if ( workers === 0 ) {
                    if ( doneCallback )
                        doneCallback();
                }
    
            }
    
            // Queue off first one
            doAsyncFunc1(1,2,3, tickWorkers);
            workers++;
    
            // Queue off second
            doAsyncFunc2(function() {
                tickWorkers();
            });
            workers++;
            
        }
    

    An async worker counter is used outside of the async function's scope to keep track of them. For each async function started, increment the counter. When they finish, the callback will decrement until they are all done and call the finished callback. This relies on the incrementing for every function. If there is a risk of the async functions failing then you can set a timeout to check every now and then for the worker count, and if it hits past a threshold and its not 0 then to fail or generate an error, as desired.

    Here is how it might look with your code:

        function doManyAsync(doneCallback) {
    
            // Async worker counters
            var workers = 0;
    
            function tickWorkers() {
                workers--;
                if ( workers === 0 ) {
                    if ( doneCallback )
                        doneCallback();
                }
    
            }
    
            // Remote Request 1
            MyController.f1(
                function(result, event){
                    tickWorkers();
                    if(event.type == 'exception') {
                        alert('Error: ' + event.message);                     
                    } else {
                        // Do stuff here such as fill in a form with the results from f1
                    }
                },
                {escape:true});
            workers++;
    
            // Remote Request 2
            MyController.f2(
                function(result, event){
                    tickWorkers();
                    if(event.type == 'exception') {
                        alert('Error: ' + event.message);                     
                    } else {
                        // Do stuff here such as fill in a form with the results from f2
                    }
                },
                {escape:true});
            workers++;
    
            // ...
    
            // Remote Request N
            MyController.fN(
                function(result, event){
                    tickWorkers();
                    if(event.type == 'exception') {
                        alert('Error: ' + event.message);                     
                    } else {
                        // Do stuff here such as fill in a form with the results from fN
                    }
                },
                {escape:true});
            workers++;
    
        }
    
        // You would then
        $.mobile.showPageLoadingMsg();
        doManyAsync(function() {
            $.mobile.hidePageLoadingMsg();
        })