Newer Version Available

This content describes an older version of this product. View Latest

Manage Synchronous Action Dependencies

When your code has dependencies that require a prior action to complete, don’t call the dependent actions until the earlier action completes. For example, render a dependent element conditionally, based on the result of the earlier action being available. Or call the dependent action from the earlier action’s callback function. This ensures that the dependent call isn’t made until after the earlier call completes.

Hidden Dependencies in a Canvas Component

When a child component calls hidden lifecycle actions, timing errors can occur more frequently with dynamic boxcar optimization. For example, this Aura component uses the fetchCanvasParameters action to retrieve values from a server-side Apex controller in its init handler, and then passes the retrieved values as parameters to a child component.

1<!-- canvasExample.cmp -->
2<aura:component controller="CanvasExampleController" >
3    <aura:attribute name="canvasParameters" type="Map"/>
4    <aura:handler name="init" value="{!this}"
5          action="{!c.fetchCanvasParameters}"/>
6
7    <force:canvasApp developerName="{!v.AppName}" scrolling="auto" 
8           width="100%" height="100%" title="{!v.title}" 
9           parameters="{!v.canvasParameters}" />
10</aura:component>

The component helper and server-side Apex controller aren’t relevant to understanding this issue, and don’t require changes. They are included after the explanation and fix, for completeness.

Note

Behind the scenes, the child <force:canvasApp> component calls its own lifecycle action getCanvasAppData to retrieve the Canvas app’s metadata. The order of these two action calls—fetchCanvasParameters and getCanvasAppData—isn’t obvious because the call to getCanvasAppData is implicit. More importantly, the order in which they return isn’t guaranteed. With standard boxcar grouping, both calls were usually grouped together in the same boxcar, which usually ensured that they completed in the correct order.

With dynamic boxcar optimization, the framework has more flexibility in how it groups actions into boxcars, using more XHR slots to send actions separately to avoid bottlenecks. It's much more likely that the two actions are sent in different boxcars. The use of separate boxcars greatly increases the possibility that the <force:canvasApp> component is instantiated and makes another call in another boxcar before the component parameters are returned in the parent component’s init handler. As you can imagine, this change in sequence can cause any number of problems.

This behavior of the framework is intentional. Even with standard boxcar grouping, it was always possible for the original code to have timing problems because of its dependency on sequential action in an asynchronous framework. Boxcar grouping behavior is an implementation detail, and the behavior changes with dynamic boxcar optimization. Design your components and apps to avoid synchronous dependencies because synchronous behavior isn’t guaranteed by the framework, regardless of boxcar implementation.

Important

The simplest and easiest way to resolve this issue is to conditionally render the <force:canvasApp> component only if the initial response values are available, that is, after the fetchCanvasParameters action has completed.

1<!--  canvasExampleFixed.cmp -->
2<aura:component controller="CanvasExampleController" >
3    <aura:attribute name="canvasParameters" type="Map"/>
4    <aura:handler name="init" value="{!this}"
5          action="{!c.fetchCanvasParameters}"/>
6
7     <aura:if isTrue="{!v.canvasParameters}">
8        <force:canvasApp developerName="{!v.AppName}" scrolling="auto" 
9           width="100%" height="100%" title="{!v.title}" 
10           parameters="{!v.canvasParameters}" />
11     </aura:if>
12
13</aura:component>

By wrapping the child <force:canvasApp> component in an <aura:if> block, the creation of the <force:canvasApp> component is deferred until after the call to the fetchCanvasParameters action completes.

For completeness, here’s the Aura component helper, which contains the fetchCanvasParameters action, and the server-side Apex controller method that returns the Canvas app parameters that the child <force:canvasApp> component can use.

1# canvasExampleHelper.js
2({
3    fetchCanvasParameters : function(component, event, helper) {
4        var action = component.get("c.getCanvasParameters");
5        action.setCallback(this, function(response) {
6            var state = response.getState();
7            if (state === "SUCCESS") {
8                component.set("v.canvasParameters",
9                    response.getReturnValue());
10            }
11        });
12        $A.enqueueAction(action);
13    }
14})
1# CanvasExampleController.apex
2public with sharing class CanvasExampleController {
3    @AuraEnabled
4    public static Map<String, Object> getCanvasParameters() {
5        // Example: return some parameters
6        Map<String, Object> params = new Map<String, Object>();
7        params.put('param1', 'value1');
8        params.put('param2', 123);
9        return params;
10    }
11}