Lightning Nested Action Call :

This chapter explains about Usage of Promises in lightning

Promises are js methodology to control nested callbacks in js
I used the same nested call example to explain the promises

Key Steps to consider when following Promise Approach :

helper.fetchAccountsHelper(  $A.getCallback(callback method )) .then( next helper method or the chained action block)
Note here in the helper method ,we wrapped it inside  var p = new Promise( $A.getCallback( function( resolve , reject ) 
also inside it's callback , we used
resolve( response.getReturnValue() ); // this is used to set the callback result , this will be passed as the value in .then() callback method


The below program shows an example of Nested Action Call in salesforce

Create an apex class and save it as ApexForActionExecution

/*


 * created by 		:		Shiva RV
 * Date				:		21-10-2018
 * Description		:		Contains methods for Nested Action Component ,also for using Promise type
 
  
*/

public class ApexForActionExecution {

@AuraEnabled    
public static Account getAccountFromName(String name)
{
    Account acc;

    try{
		acc=[select id from account where name=:name limit 1];
    }
    catch(Exception e)
    {
        acc=null;
        throw  new AuraHandledException('No Account is present');
    }
    return acc;
}
@AuraEnabled    
public static List<opportunity> getOpportunitiesFromAccount(Id AccountId)
{
   List<opportunity> opportunityList;

    try{
		opportunityList=[select id,name from opportunity where accountid=:AccountId limit 5];
    }
    catch(Exception e)
    {
        opportunityList=null;
    }
   return opportunityList;
}  
  @AuraEnabled    
public static List<Lead> getLeadsFromAccount(Id AccountId)
{
    List<Lead> leadList;

    try{
		leadList=[select id,name,shivalight__customAccountLookup__c from lead where shivalight__customAccountLookup__c=:AccountId limit 5];
    }
    catch(Exception e)
    {
        leadList=null;
        
    }
    return leadList;
  }    
}

Create the Lightning Event as below  and save it as  NestedActionComponent

<!--


 * created by 		:		Shiva RV
 * Date				:		21-10-2018
 * Description		:		action which need's to be executes after completion 
							of another action in lightning
      				  /* Hypothetical Scenerio :
						 First fetch FewAccount and then
						 Fetch all opportunity,Leads of the chosen account using Promises style
  
-->

<aura:component controller="ApexForActionExecution">
    
    <aura:attribute name="accountName" type="String"></aura:attribute>
    <aura:attribute name="AccountRecord" type="Account"></aura:attribute>
    <aura:attribute name="associatedOpps" type="List"></aura:attribute>
    <aura:attribute name="associatedLeads" type="List"></aura:attribute>

    <aura:attribute name="isResultCompleted" type="Boolean" default="false"></aura:attribute>

    <lightning:input type="text" name="acountName" value="{!v.accountName}" label="Account Name" />
	<lightning:Button name="fetch Accounts and Related Records" label="fetch Accounts" onclick="{!c.fetchAccounts}"></lightning:Button>
    
    <aura:if isTrue="{!v.isResultCompleted}">
  	<h1>Opportunity Lists</h1>
    <table>
    	  <tr>
     	  	<th>Id</th>
            <th>Opportunity Name</th>
          </tr>
    	  <aura:Iteration items="{!v.associatedOpps}" var="indOpp" >
      	  <tr>
           <td>{!indOpp.Id}</td>
           <td>{!indOpp.Name}</td>
          </tr>
          </aura:Iteration>
      </table>
      <br/>
  	
     <h1>Lead Lists</h1>
     <table>
     <tr>
     <th>Id</th>
     <th>Lead Name</th>
     </tr>
     <aura:Iteration items="{!v.associatedLeads}" var="indLead">
     <tr>           
 	 <td> {!indLead.Id}</td>
     <td> {!indLead.Name}</td>
     </tr>
     </aura:Iteration>
	 </table>
        <br/>
    </aura:if>
</aura:component>
NestedActionComponent Js Controller Code:
({
	fetchAccounts : function(component, event, helper) {
        var chosenSubRecordType=component.get("v.chosenTypeOfSubObject");
        component.set("v.chosenTypeOfSubObject",chosenSubRecordType);
        console.log(chosenSubRecordType);
        var accountName=component.get("v.accountName");
        //return if any of the value is null;
        //error handling has a new section allocated, for now just keep it as alert box
        if( !accountName)
        {
            alert("please enter chosenSubRecordType and accountName fields");
            
            return;
        }
        component.set("v.isResultCompleted",false);
        helper.fetchAccountsHelper(component,chosenSubRecordType,accountName,helper).then(
            
             $A.getCallback(function(result) {
                 console.log("result fetchaccountPromise "+result);
                 component.set("v.AccountRecord",result);
                 console.log("result "+component.get("v.AccountRecord").Id);
           	  	 return helper.fetchLeadsHelper(component,component.get("v.AccountRecord").Id);
		     })
        	

        ).then(
            $A.getCallback(function(result) {
                console.log("leads callback ");
                component.set("v.associatedLeads",result);
	       		return helper.fetchOpportunitiesHelper(component,component.get("v.AccountRecord").Id);
		     })
        ).then(
            
             $A.getCallback(function(result) {
                console.log("Opps callback ");
                component.set("v.associatedOpps",result);
	       		console.log("in associate Opps callback "); 
                component.set("v.isResultCompleted",true);
 
		     })
        
        ).catch(function(error) {
            var errorDesc="";error[0].message
            for(var i=0;i<error.length;i++)
                errorDesc+=("error no : "+ (i+1) +" reason "+error[i].message);
            alert(errorDesc);
        	//$A.reportError("Error ", error);
            
    	});    
		
    },
    
   
})
NestedActionComponent Helper Controller Code:
({
	fetchAccountsHelper : function(component,chosenSubRecordType,accountName,helper) {
        
        var p = new Promise( $A.getCallback( function( resolve , reject ) { 
        var action                          = component.get("c.getAccountFromName");
        action.setParams( {"name" : accountName} );
        action.setCallback(this, function(response)
         {
         	var state = response.getState();
         	if (state === "SUCCESS") 
         	{
                console.log(response.getReturnValue());
                resolve( response.getReturnValue() );

              // component.set("v.AccountRecord",response.getReturnValue());
               //helper.helperLevel2Function(component,chosenSubRecordType,response.getReturnValue().Id);
                
         	}
         	else
         	{
                 reject( response.getError() );
         	}    
         });
         $A.enqueueAction(action);
    }));            
    return p;
		
         
	},
    
    fetchOpportunitiesHelper : function(component,accountId)
    {
        console.log("in fetchOpportunitiesHelper ");
        var p = new Promise( $A.getCallback( function( resolve , reject ) { 
        var action = component.get("c.getOpportunitiesFromAccount");
        action.setParams( {"AccountId" : accountId} );
        action.setCallback(this, function(response)
         {
         	var state = response.getState();
         	if (state === "SUCCESS") 
         	{
            	console.log(response.getReturnValue());
                resolve( response.getReturnValue() );
            	//component.set("v.associatedOpps",response.getReturnValue());
            	//helper.helperLevel2Function(component,chosenSubRecordType,response.getReturnValue().Id);
                
         	}
         	else
         	{
                 reject( response.getError() );
         	}    
         });
         $A.enqueueAction(action);
    	 }));            
    	 return p;      
	},
	fetchLeadsHelper: function(component,accountId)
	{
		console.log("in fetchOpportunitiesHelper ");
		var p = new Promise( $A.getCallback( function( resolve , reject ) { 
		var action = component.get("c.getLeadsFromAccount");
		action.setParams( {"AccountId" : accountId} );
    	action.setCallback(this, function(response)
    	{
    		var state = response.getState();
        	if (state === "SUCCESS") 
        	{
        		console.log(response.getReturnValue());
                resolve( response.getReturnValue() );
                
				//component.set("v.associatedLeads",response.getReturnValue());
        	}
      	else
      	{
             reject( response.getError() );
     	}    
    	});
    	$A.enqueueAction(action);
		}));            
    	return p;  
	}
})
Below is the hello world App code:
<!--


 * created by 		:		Shiva RV
 * Date				:		13-10-2018
 * Description		:		Hello World Lighting App
  
-->

<aura:application extends="force:slds">
 <!--
	this is the lightning app where you can place html codes , associate js code
	also can embed lightning component to it 
 -->
    
  <!--Nested Action  and error handling using promise-->
    <c:NestedActionUsingPromiseComponent></c:NestedActionUsingPromiseComponent>

</aura:application>
Output:

Key Notes :
*If two actions are run independently , we can't guarantee the execution order
*Hence if next apex method 2 call must be made inside the callback of method1 as explained in the above program