106

I need to get the response.data out of the promise so it can be returned by the enclosing function. I know, I probably can't do it the way I've coded it because of normal JavaScript scope. Is there any way, it can be done?

The console.log at #1 produces the correct data. console.log #2 always produces 'a';

function addSiteParentId(nodeId) {   
    var theParentId = 'a';
    var parentId = relationsManagerResource.GetParentId(nodeId)
                        .then(function(response){                               
                            theParentId = response.data;
                            console.log(theParentId);  // #1
                        });
    console.log(theParentId);  // #2
    return theParentId;
}

Any pointers would be appreciated.

3
  • 3
    No - you cannot - any operations you want to do with response must go in the .then() handler. You can't write asynchronous Javascript in a synchronous style! Commented May 30, 2016 at 21:43
  • 3
    This isn't about returning data from a promise. The second console.log happens before the promise sets theParentId variable - because the promise is async while the console.log is not.
    – omarjmh
    Commented May 30, 2016 at 21:44
  • 1
    @Amit, yes I did but there were only complaints about the question not being specific enough, which was true.
    – Craig
    Commented May 30, 2016 at 23:22

3 Answers 3

112

One of the fundamental principles behind a promise is that it's handled asynchronously. This means that you cannot create a promise and then immediately use its result synchronously in your code (e.g. it's not possible to return the result of a promise from within the function that initiated the promise).

What you likely want to do instead is to return the entire promise itself. Then whatever function needs its result can call .then() on the promise, and the result will be there when the promise has been resolved.

Here is a resource from HTML5Rocks that goes over the lifecycle of a promise, and how its output is resolved asynchronously:
https://web.dev/promises/

4
  • 12
    Many thanks for the gentle guidance. I knew it would be something to do with the way I approached it. I'm very new to Angular so was hitting a brick wall. It's now working but can probably be further refined once the panic's over. Thanks again for the advice.
    – Craig
    Commented May 30, 2016 at 23:20
  • 1
    @Maximillian I'd suggest that answering the question as asked would also have value. Commented Apr 7, 2020 at 11:22
  • 1
    @donothingsuccessfully As far as I can tell, I did answer the question as asked. Commented Apr 8, 2020 at 20:04
  • @Maximillian apologies Commented Apr 9, 2020 at 10:35
13

I also don't like to use a function to handle a property which has been resolved again and again in every controller and service. Seems I'm not alone :D

Trying to get result with a promise as a variable will not work. But I found and use a solution below to access to the result as a property.

Firstly, write result to a property of your service:

app.factory('your_factory',function(){
    var theParentIdResult = null;
    var factoryReturn = {  
        theParentId: theParentIdResult,
        addSiteParentId : addSiteParentId
    };
    return factoryReturn;
    function addSiteParentId(nodeId) {   
         var theParentId = 'a';
         var parentId = relationsManagerResource.GetParentId(nodeId)
             .then(function(response){                               
                 factoryReturn.theParentIdResult = response.data;
                 console.log(theParentId);  // #1
             });                    
    }        
})

Now, we just need to ensure that method addSiteParentId always be resolved before we accessed to property theParentId. We can achieve this by using some ways.

  • Use resolve in router method:

      resolve: {
          parentId: function (your_factory) {
               your_factory.addSiteParentId();
          }
      }
    

then in controller and other services used in your router, just call your_factory.theParentId to get your property. Referce here for more information: http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx

  • Use run method of app to resolve your service.

      app.run(function (your_factory) { your_factory.addSiteParentId(); })
    
  • Inject it in the first controller or services of the controller. In the controller we can call all required init services. Then all remain controllers as children of main controller can be accessed to this property normally as you want.

Chose your ways depend on your context depend on scope of your variable and reading frequency of your variable.

1

You have to return a promise instead of a variable. So in your function just return:

return relationsManagerResource.GetParentId(nodeId)

And later resolve the returned promise. Or you can make another deferred and resolve theParentId with it.

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