2

I have a lightning application that is being used in both lightning and classic (via ltng:outApp).

I have a series of callbacks that work perfectly in lightning, but when invoked from classic, the second action.setCallback() callback is never called, and when I monitor action.getstate() it returns "NEW" indefinitely.

Right now, the first call to the "c.getMailingPeriods" controller action works and the data is returned.

Then, the call to "c.getQuoteLines" is called and enqueued, but is never executed and the callback is never called.

If I first call "c.getQuoteLines", then it works and the subsequent action then never gets executed. (I have confirmed the params are all set properly with the correct values)

I have attempted to wrap in $A.setCallback(), without success.

Again, this works in lightning, but in classic the enqueued action never gets attempted to be submitted to the server.

Controller

...
helper.getMailingPeriods(component)
.then(function(mailingPeriods) {
    component.set('v.mailingPeriods', mailingPeriods);

    // Get quote lines for bundle, with subquery of previously selected NTAs
    helper.getQuoteLines(component, quoteId, quoteLineId)
    .then(function(quoteLines) {
        ...

Helper

...
getQuoteLines: function(component, quoteId, quoteLineId) {
    return new Promise(function (resolve, reject) {
       // Get jobs from controller
       var action = component.get("c.getQuoteLines");

        action.setParams({
            "quoteId":  quoteId,
            "bundleQuoteLineId": quoteLineId,
        });

       action.setCallback(component, function(results) {
           utils.log('getQuoteLines() results', results);
           if(results.getState() === 'SUCCESS') {
               utils.log('results:', results.getReturnValue());
               resolve(results.getReturnValue());
           } else if (results.getState() === 'ERROR') {
               utils.log('getQuoteLines() ERROR', results.getError());
               $A.log("Errors", results.getError());
               reject(results.getError());
           }
       });

       $A.enqueueAction(action);

   });
},

getMailingPeriods: function(component) {
    return new Promise(function (resolve, reject) {
       // Get jobs from controller
       var action = component.get("c.getMailingPeriods");

       action.setCallback(this, function(results) {
           utils.log('getMailingPeriods() results', results);
           if(results.getState() === 'SUCCESS') {
               utils.log('results:', results.getReturnValue());
               resolve(results.getReturnValue());
           } else if (results.getState() === 'ERROR') {
               utils.log('getMailingPeriods() ERROR', results.getError());
               $A.log("Errors", results.getError());
               reject(results.getError());
           }
       });

       $A.enqueueAction(action);
   });
},
...
  • I had a problem of lost callbacks when the requesting component was replaced in the body of another component before the callback had completed. Mentioning it here in case there is some marginal timing in your app that only surfaces in the Lightning Out case. – Keith C Jul 11 '18 at 8:35
  • Thanks for the input. I don't think that I have anything replacing the content of my component, as this is the very top level component. I noticed some odd behavior, when I refresh the page, I can see that my debug entries show up momentarily right before the refresh happens (meaning that the callback finally get's called and is returned).... I am not sure what might be holding it up from executing. – Austin Turner Jul 11 '18 at 13:59
3

It appears that helper.getQuoteLines( should have a return in front of it so that the promise chain resolves correctly. As written, getQuoteLines will not have resolved when the following .then( is evaluated.

Maybe it is just because there is so much of the controller code trimmed - update the question if this assumption is incorrect.

I would expect that the chain might look similar to this:

helper.getMailingPeriods(component)
    .then(function (mailingPeriods) {
        component.set('v.mailingPeriods', mailingPeriods);

        // Get quote lines for bundle, with subquery of previously selected NTAs
        return helper.getQuoteLines(component, quoteId, quoteLineId)
    })
    .then(function (quoteLines) {
        // quotelines stuff
        return resultOfQuoteLinesStuff;
    })
    .then(function (resultOfQuoteLines) {
        // anything you needed to do with the results of the quote lines
        return foo;
    })
    .catch(function (err) {
        console.log(err);
    });
  • 1
    Thank you for the suggestion. I am not chaining .then's together and all of my logic is nested in each function, and that should not matter as it is identical. The problem is that SFDC never calls the callback from action.setCallback() - SFDC never sends the job to the server. I have used setTimeout() to monitor the action using action.getState() and it stays at "NEW" forever, which tells me the problem is related to the Aura framework and not related to the promises. – Austin Turner Jul 11 '18 at 0:38
0

After a long trek with SFDC support (involving refactoring my code to reproduce the issue with less than 200 lines of code so they were allowed to "look at it"), I was able to figure out a solution, but I am still unclear why this is an issue here and not in other places in the codebase (or other codebases I have built in classic to work with lightning).

The issue was that when a promise is used, the promise anonymous function should be wrapped in $A.getCallback() to ensure it will work in all cases. I am not sure why I have never had problems in the past without wrapping in $a.getCallback().

For reference - https://developer.salesforce.com/docs/atlas.en-us.lightning.meta/lightning/js_promises.htm

({
    getMailingPeriods: function(component) {
        return new Promise($A.getCallback(function (resolve, reject) {
           var action = component.get("c.getMailingPeriods1");
           action.setCallback(this, function(results) {
               console.log('getMailingPeriods() results', results);
               console.log('results.getState():', results.getState());
               console.log('results.getReturnValue():', results.getReturnValue());
               resolve(results.getReturnValue());
           });
           $A.enqueueAction(action);
       }));
    },
    getQuoteLines: function(component, quoteId, quoteLineId) {
        return new Promise($A.getCallback(function (resolve, reject) {
            var action = component.get("c.getQuoteLines");
            action.setParams({ "quoteId":  quoteId, "bundleQuoteLineId": quoteLineId, });
            console.log('params - sanity check', { "quoteId":  quoteId, "bundleQuoteLineId": quoteLineId, });
            action.setCallback(this, function(results) {
               console.log('getQuoteLines() results', results);
               console.log('results.getState():', results.getState());
               console.log('results.getReturnValue():', results.getReturnValue());
               resolve(results.getReturnValue());
            });
            console.log('action about to be enqueued');
            $A.enqueueAction(action);
            console.log('enqueue action method called');
       }));
    },
})

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.