50

My service is:

myApp.service('userService', [
  '$http', '$q', '$rootScope', '$location', function($http, $q, $rootScope, $location) {
    var deferred;
    deferred = $q.defer();
    this.initialized = deferred.promise;
    this.user = {
      access: false
    };
    this.isAuthenticated = function() {
      this.user = {
        first_name: 'First',
        last_name: 'Last',
        email: 'email@address.com',
        access: 'institution'
      };
      return deferred.resolve();
    };
  }
]);

I'm calling this in my config file via:

myApp.run([
  '$rootScope', 'userService', function($rootScope, userService) {
    return userService.isAuthenticated().then(function(response) {
      if (response.data.user) {
        return $rootScope.$broadcast('login', response.data);
      } else {
        return userService.logout();
      }
    });
  }
]);

However, it complains that then is not a function. Aren't I returning the resolved promise?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
user3620820
  • 513
  • 1
  • 4
  • 4

7 Answers7

106

How to simply return a pre-resolved promise in AngularJS

Resolved promise:

return $q.when( someValue );    // angularjs 1.2+
return $q.resolve( someValue ); // angularjs 1.4+, alias to `when` to match ES6

Rejected promise:

return $q.reject( someValue );
Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
  • 2
    This is exactly what I was looking for. A nice short way to return a static value when stubbing out http service calls. – Richard Oct 12 '15 at 12:01
35

Return your promise , return deferred.promise.
It is the promise API that has the 'then' method.

https://docs.angularjs.org/api/ng/service/$q

Calling resolve does not return a promise it only signals the promise that the promise is resolved so it can execute the 'then' logic.

Basic pattern as follows, rinse and repeat
http://plnkr.co/edit/fJmmEP5xOrEMfLvLWy1h?p=preview

<!DOCTYPE html>
<html>

<head>
  <script data-require="angular.js@*" data-semver="1.3.0-beta.5" 
        src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
  <link rel="stylesheet" href="style.css" />
  <script src="script.js"></script>
</head>

<body>

<div ng-controller="test">
  <button ng-click="test()">test</button>
</div>
<script>
  var app = angular.module("app",[]);

  app.controller("test",function($scope,$q){

    $scope.$test = function(){
      var deferred = $q.defer();
      deferred.resolve("Hi");
      return deferred.promise;
    };

    $scope.test=function(){
      $scope.$test()
      .then(function(data){
        console.log(data);
      });
    }      
  });

  angular.bootstrap(document,["app"]);

</script>

serge
  • 13,940
  • 35
  • 121
  • 205
mccainz
  • 3,478
  • 5
  • 35
  • 45
  • How will the promise get resolved then? The `.then` will never get triggered – Shamoon May 09 '14 at 14:43
  • @Shamoon deferred.resolve(); is resolved in the moment you call it because there is no value given in. I would suggest instead of write deferred.resolve("Hi"); just write deferred.resolve(); to get a resolved empty promise. But you could also use $q.when(); to get a resolved thenable empty promise ;-). – Sebastian Sep 23 '14 at 06:33
  • IMHO this is better than the accepted answer, which by using $timeout to generate a promise that will be resolved is failing to show the mechanics of manually creating and resolving promises. – sifriday Sep 01 '15 at 14:14
21

From your service method:

function serviceMethod() {
    return $timeout(function() {
        return {
            property: 'value'
        };
    }, 1000);
}

And in your controller:

serviceName
    .serviceMethod()
    .then(function(data){
        //handle the success condition here
        var x = data.property
    });
Brocco
  • 62,737
  • 12
  • 70
  • 76
  • @BenjaminGruenbaum Yes, you are correct, I was unaware that `$timeout` actually returns a promise, serviceMethod can be shortened to: return $timeout(function() { return {property: 'value'}; }, 1000); – Brocco May 09 '14 at 18:46
  • 15
    This may work, but it's certainly not a recommended approach for returning promises – Mike Robinson May 09 '14 at 18:53
  • @BenjaminGruenbaum I did, now returning the promise which $timeout generates. – Brocco May 09 '14 at 19:04
  • Very well, I retracted my -1. – Benjamin Gruenbaum May 09 '14 at 19:05
  • I tend to agree with Mike, this might work, but the answer below from mccainz better explains the mechanics of creating, resolving and returning a promise. I've added a downvote for this reason. – sifriday Sep 01 '15 at 14:15
  • @sifriday liking a different answer does not mean another answer is bad. This example shows returning a contrived example of returning a promise, not a way to create promises. Mike's comments were in regards to me using `$q.defer()` instead of utilizing the promise that is generated with $timeout. – Brocco Sep 01 '15 at 14:22
  • 3
    ah ok, but I still agree with his sentiment even if for a different reason! I appreciate a downvote is disheartening, and that we could just use upvotes to help the good content float to the top, but in this case I do feel your answer is not helpful, because it fails to explain the mechanics of $q and instead presents an abstraction of $q. Someone new to promises, or like me familiar with promises but new to Angular, could get totally the wrong end of the stick! – sifriday Sep 01 '15 at 14:29
4

Here's the correct code for your service:

myApp.service('userService', [
  '$http', '$q', '$rootScope', '$location', function($http, $q, $rootScope, $location) {

    var user = {
      access: false
    };

    var me = this;

    this.initialized = false;
    this.isAuthenticated = function() {

      var deferred = $q.defer();
      user = {
        first_name: 'First',
        last_name: 'Last',
        email: 'email@address.com',
        access: 'institution'
      };
      deferred.resolve(user);
      me.initialized = true;

      return deferred.promise;
    };
  }
]);

Then you controller should align accordingly:

myApp.run([
  '$rootScope', 'userService', function($rootScope, userService) {
    return userService.isAuthenticated().then(function(user) {
      if (user) {
        // You have access to the object you passed in the service, not to the response.
        // You should either put response.data on the user or use a different property.
        return $rootScope.$broadcast('login', user.email);  
      } else {
        return userService.logout();
      }
    });
  }
]);

Few points to note about the service:

  • Expose in a service only what needs to be exposed. User should be kept internally and be accessed by getters only.

  • When in functions, use 'me' which is the service to avoid edge cases of this with javascript.

  • I guessed what initialized was meant to do, feel free to correct me if I guessed wrong.

haimlit
  • 2,572
  • 2
  • 22
  • 26
2

To return a resolved promise, you can use:

return $q.defer().resolve();

If you need to resolve something or return data:

return $q.defer().resolve(function(){

    var data;
    return data;

});
1

For shorter JavaScript-Code use this:

myApp.service('userService', [
  '$q', function($q) {
    this.initialized = $q.when();
    this.user = {
      access: false
    };
    this.isAuthenticated = function() {
      this.user = {
        first_name: 'First',
        last_name: 'Last',
        email: 'email@address.com',
        access: 'institution'
      };
      return this.initialized;
    };
  }
]);

You know that you loose the binding to userService.user by overwriting it with a new object instead of setting only the objects properties?

Here is what I mean as a example of my plnkr.co example code (Working example: http://plnkr.co/edit/zXVcmRKT1TmiBCDL4GsC?p=preview):

angular.module('myApp', []).service('userService', [
    '$http', '$q', '$rootScope', '$location', function ($http, $q, $rootScope, $location) {
    this.initialized = $q.when(null);
    this.user = {
        access: false
    };
    this.isAuthenticated = function () {
        this.user.first_name = 'First';
        this.user.last_name = 'Last';
        this.user.email = 'email@address.com';
        this.user.access = 'institution';
        return this.initialized;
    };
}]);

angular.module('myApp').controller('myCtrl', ['$scope', 'userService', function ($scope, userService) {
    $scope.user = userService.user;
    $scope.callUserService = function () {
        userService.isAuthenticated().then(function () {
            $scope.thencalled = true;
        });
    };
}]);
Sebastian
  • 2,874
  • 25
  • 31
0

Try this:

myApp.service('userService', [
    '$http', '$q', '$rootScope', '$location', function($http, $q, $rootScope, $location) {
      var deferred= $q.defer();
      this.user = {
        access: false
      };
      try
      {
      this.isAuthenticated = function() {
        this.user = {
          first_name: 'First',
          last_name: 'Last',
          email: 'email@address.com',
          access: 'institution'
        };
        deferred.resolve();
      };
    }
    catch
    {
        deferred.reject();
    }

    return deferred.promise;
  ]);
premsh
  • 213
  • 3
  • 6
  • 16