1

so I'm working on a project where I have a dataset and I have to retrieve various statistics on said dataset depending on user input by retrieving data from a server.

I'm trying to set up a AngularJS service which has various methods that return various information on a given data point.

Here is a public method from the service:

function test() {
    return $http.get('blahblah.api.../datapoint1')
    .success(function(data) {
        console.log(data.name);
        return data.name;
    });
}

Here is the relevant part from the controller:

self.data = service.test();
console.log(self.data);

Interesting enough, the console.log(data.name) within the success of the http call gives me exactly what I want. The name of the given data point. However the console.log(self.data) outside of its scope gives me the original promise object. Any method I use to send information outside of the scope of "success" gives me a promise object. Why is this? And is there any way of getting around this? It seems awfully inefficient to have to deal with the error handling in the controller.

Vangogh500
  • 939
  • 1
  • 7
  • 17

3 Answers3

2

You're not creating a new promise for your function.

Look at your test function. See how you have two returns? The first time, you return the $http.get function, which is a promise, but a promise that resolves to the success function. What you return from the success function doesn't resolve that promise -- it's already been resolved in the success function.

Instead, you need to create a new promise for your service function and return that to your app.

Here's how you should structure your request

function test() {
    // Setup a promies for this function to return
    var deferred = $q.defer()

    $http.get('blahblah.api.../datapoint1')
    .success(function(data) {
        console.log(data.name);

        // Return our data to use in our app
        deferred.resolve(data);
    });

    // Return the promise
    return deferred.promise
}

Implementation

service.test().then(function(data){
      self.data = data
      console.log(self.data.name) //should be the same as in your service
});

Note that you'll need to include the $q injector in your service.

Seamus James
  • 981
  • 3
  • 12
  • 25
  • There's absolutely no reason to create a second promise. $http returns one already. – Kevin B Aug 24 '15 at 21:56
  • That's untrue: http://www.mattgreer.org/articles/promises-in-wicked-detail/#chaining-promises – Alfonso Presa Aug 24 '15 at 21:57
  • Just curious why would you make a new promise? Wouldn't that just mean more stuff to process? Why not just return the original promise from the http.get(). Or do we do that so we can also deal with error handling in the service as well as the controller? – Vangogh500 Aug 24 '15 at 21:59
  • Not more stuff to process, it's just not necessary. – Kevin B Aug 24 '15 at 21:59
  • You certainly don't have to, but it's the pattern advocated by the documentation: https://docs.angularjs.org/api/ng/service/$q. My only issue with Kevin's implementation above is that you end up with at least two `then/catch` functions. If all that you're doing is returning the value retrieved by `$http`, you may as well handle that directly in whatever code is consuming that service. – Seamus James Aug 24 '15 at 22:42
1

No, it is not possible to get that value to the outer scope in any meaningful way until after the promise has resolved. You'll have to access it from within a callback. As far as error handling, that can of course still be handled by your service.

function test() {
    return $http.get('blahblah.api.../datapoint1')
    .then(function(response) {
        console.log(response.data.name);
        return response.data.name;
    }).catch(function (e) {
        // error handling...
    });
}
self.data = ''; // default value
service.test().then(function (name) {
    self.data = name;
    console.log(self.data); // correct value
});
console.log(self.data); // still default value

I'd suggest moving away from using .success() and .error() in favor of .then() and .catch(), the former two have been deprecated in recent versions of AngularJS.

Kevin B
  • 94,570
  • 16
  • 163
  • 180
0

There's no way to make that service synchronous, you should always use a promise.

Try this:

function test() {
    return $http.get('blahblah.api.../datapoint1')
    .success(function(data) {
        console.log(data.name);
        return $q.resolve(data.name);
    });
}

And then this:

service.test().then(function (name) {
    self.data = name;
    console.log(self.data);
})

Edit:

About why you're getting a promise think that your code is the same as the following:

function something(data) {
   console.log(data.name);
   return data.name;
}

function test() {
    return $http.get('blahblah.api.../datapoint1')
    .success(something);
}

Your code is returning the result of the invocation to success that is indeed a promise.

Alfonso Presa
  • 1,024
  • 7
  • 11
  • I see. Thanks! Any specific reason why it can't be made synchronous? Or a link explaining why? I'm assuming that there is good reason to have to do it this way. – Vangogh500 Aug 24 '15 at 21:45
  • Sure: http://stackoverflow.com/questions/13088153/how-to-http-synchronous-call-with-angularjs. Also note that javascript is by definition asynchronous, so even if it was possible (wich it is if you use Jquery or plain old XHR, but it's a deprecated behaviour) it not recommendable at all – Alfonso Presa Aug 24 '15 at 21:46