I'm developing a component that calls an Apex method which performs a callout to our integration platform (generates an SAP customer from a salesforce account). The component is a button placed on the account record page; when clicked (and user confirms) it's supposed to wait for a response from the integration. The strange thing is that the first time the button gets clicked, seems to work fine (spinner goes off and then a toast is displayed upon completion), however if I click it again, the toast appears before the callout is completed (I can tell this because the integration process console shows me it's still running). I'm pretty sure I have my promises set up correctly, can anyone tell me what seems to be missing? Apex:

public with sharing class GenerateSAPCustomerAura_Ctrl {
    @AuraEnabled (cacheable=true)
    public static String generateSAPCustomer(String recordId){
        String responseJson = IicsRestUtility.GenerateSAPCustomer(recordId);
        return responseJson;

(IicsRestUtility is a utility class that handles the HttpRequest/HttpResponse stuff)

relevant part of the controller:

    handleConfirm : function(component,event,helper){


    callApex : function( component, helper, apexAction, params ) {
        // generic server side method call function.  returns a promise
        return new Promise($A.getCallback(function(resolve, reject){ 
            var action = component.get("c." + apexAction + "");
            action.setCallback(this, function(callbackResult) {
                if (callbackResult.getState() == 'SUCCESS'){
                    resolve({"c":component, "h":helper, "r" :callbackResult.getReturnValue()});
                if (callbackResult.getState() == 'ERROR'){
                    reject({"c":component, "h":helper, "r" :callbackResult.getError()});
    generateSAPCustomer : function(component,helper) {
        component.set("v.response","Processing Request...");
        var recId = component.get("v.recordId");
        this.callApex(component, helper, "generateSAPCustomer",{recordId : recId})
                const msg = JSON.parse(retVal.r);
                const cmp = retVal.c;
                const hlp = retVal.h;
                if(typeof(msg.response.REMARKS) !== "undefined"){
                if ((typeof(msg.response.SAP_CUSTOMER_NO) !== "undefined") 
                    && !(msg.response.SAP_CUSTOMER_NO === null)
                    var toastEvent = $A.get("e.force:showToast");
                        "title": "Success",
                        "message": msg.response.REMARKS + " SAP Id: " + msg.response.SAP_CUSTOMER_NO,
                        "type": "success",
                        "mode": "sticky"
                } else {
                    var toastEvent = $A.get("e.force:showToast");
                        "title": "Error",
                        "message": msg.response.REMARKS,
                        "type": "error",
                        "mode": "sticky"
                    console.error("generateSAPCustomer error: " + JSON.stringify(msg));
                console.error("generateSAPCustomer catch error: " + error);
                $A.reportError("There has been an unexpected error :( - " + error);

the callApex function came from somewhere on the googles, and seems to work in other components I've used it in.

Thanks in advance.


1 Answer 1


This happens because cacheable=true. When you do this, the platform notices that there's already a cached response and uses that instead of waiting. It will then callback again later, but the promise will have already resolved (and they can't resolve multiple times!). Set cacheable=false to make sure that it waits until the process is finished. Or, if possible, you can set up the CSP (Content Security Policy) to allow direct access to that server, and you could call the server directly via fetch or XMLHttpResponse.

  • 1
    well I'll be hornswaggled. For some reason I didn't think you were allowed to set it to / it ignored false. Thanks! Apr 17, 2020 at 20:11
  • @LeapinDave No problem. I know that the compiler previously had rules about what was cacheable, but I think that was either a bug, or they relaxed the restrictions.
    – sfdcfox
    Apr 17, 2020 at 20:15

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .