Lightning multiple enqueued actions execute action when all are done

As we know enqueueing multiple actions are all sent to the server at the same time.

What is the suggested approach when need to load multiple data in initialization for lightning component

The issue I am having is that the time each action takes to complete is not the same. Lets say I want to show a spinner until the all actions have completed. How can I tell that all are done.

The setup is basically:

doInit: function (component, event, helper) {

    var action1 = component.get(.....);

    action1.setCallback(this, function (result) {
        ......
    });

    var action2 = component.get(.....);

    action2.setCallback(this, function (result) {
        ......
    });

    var action3 = component.get(.....);

    action3.setCallback(this, function (result) {
        ......
    });


    $A.enqueueAction(action1);
    $A.enqueueAction(action2);
    $A.enqueueAction(action3);

}

Now I want to do this when and only when ALL 3 actions have completed:

$A.util.addClass(component.find("spinner"), "slds-hide");

Assuming the length of time it takes to make the roundtrip for each action can be variable and unpredictable just placing it inside a give callback may remove the spinner while other actions are still processing.

So the question, where to put that line to ensure it is only done once all actions have made the roundtrip?

Answer

Eric, I like to use Promises for that. This is my preferred pattern

First Define each promise (I do this in the helper)

myPromise1 : function (component) {
        var action = component.get('c.MyMethod');
        var myParam1 = component.get("v.myParam1");
        var myParam2 = component.get("v.myParam2");

        return new Promise(function (resolve, reject) {
            action.setParams({
                myParam1: myParam1,
                myParam2: myParam2
            });

            action.setCallback(this, function (response) {
               var state = response.getState();

                if (component.isValid() && state === "SUCCESS") {
                    resolve(response.getReturnValue());
                }
                else if (component.isValid() && state === "ERROR") {
                    var errors = response.getError();
                    reject(response.getError()[0]);
                }
            });

            $A.enqueueAction(action);
        });
    },

Then call the promises

Promise.all([this.myPromise1(component), this.myPromise2(component)]).then(function(results) {
            var p1Results = results[0]; //Results from Promise 1
            var p2Results = results[1]; //Results from Promise 2

           //Do your thing
        }).catch(function (err) {
          //Handle errors on any promise here
        });

I love the idea of having just one error handler, and the results come neatly in an array (ordered in the same ordered you called the promises in your first line above)

=== Additional Information ===

If you ever want to have a reusable promise (but don’t want to call more than 1 thing with it), you can define it as above and then call it like a function

this.myPromise1(component).then(function(results) {
            var resultsGoHere = results;

            //Do your thing
        }).catch(function (err) {
            //Handle Error
        });

Here are a few resources I loved (the first one was where I learned, the next two where I made sure not to mess up). There is too much info to copy (the relevant parts are above anyway) but hopefully you can get some ideas for more complex patterns (like the Promise.race);

Attribution
Source : Link , Question Author : Eric , Answer Author : Sebastian Kessel

Leave a Comment