Question: Return promise inside resolve function as its continuation

Question

Return promise inside resolve function as its continuation

Answers 2
Added at 2016-12-28 18:12
Tags
Question

Context

I'm working with Angular. I have a service called UserService, that handles login, authentication and user data requests.

The get method needs to check if the user has a valid (not expired) token for authentication before making the get request. So, if it has, make the request, if not, request a token and then make the request.

Problem

This get method needs to hide its complex requests. It has to return only a Promise as it was making only one request.

So, an example of usage:

UserService
    .get()
    .then(data => { ... })
    .catch(error => { ... })

Wrong solution

Check if the token is expired. If it is, return a request to refresh the token, and there, make and return the get request. If it is not, just make and return the get request. As below:

function get() {
    if (isTokenExpired(token))
        return $http
            .post(url + '/refreshtoken', 'token=' + refreshToken)
            .then(response => {
                token = response.data.token
                return $http.get(url + '?token=' + token)
            })
            .catch(response => { ... })
    else
        return $http.get(url + '?token=' + token)
}

But it's returning a promise that I will have to handle like this:

UserService
    .get()
    .then(request => {
        request // THAT IS NOT GOOD
            .then(data => { ... })
            .catch(error => { ... })
    })
    .catch(error => { ... })

Anything like the usage example!

Correct solution

How to make this get method on service, which handles authentication like that and hides everything from the controller that will use, letting it like in the usage example?

Answers to

Return promise inside resolve function as its continuation

nr: #1 dodano: 2016-12-28 19:12

Here's an example. Basically you need to transform with an additional .then()

angular.module('app', [])
  .service('UserService', function($http) {
    let url = 'https://jsonplaceholder.typicode.com';

    function getUserData(token) {
      return $http.get(url + '/users/1', {
          token: token
        })
        .then(resp => resp.data);
    }

    this.get = function(tokenIsValid) {
      if (!tokenIsValid){
        console.log('refreshing');
        return $http.post(url + '/posts', {
            refreshToken: 'rt'
          })
          .then(getUserData)
        }
      return getUserData();
    }
    return this;
  })
  .run(function($rootScope, UserService) {
    $rootScope.get = (valid) => {
      console.log('Getting with ' + (valid? '':'in') + 'valid token');
      UserService.get(valid)
        .then(console.log)
        .catch(console.error);
    }
  });
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
  <button ng-click="get()">Get invalid</button>
  <button ng-click="get(true)">Get valid</button>
</div>

nr: #2 dodano: 2016-12-28 20:12

But it's returning a promise for a promise that I will have to handle like this:

 UserService.get().then(request => { request.then(data => { … }) })

No.

Promises do chain, any "promise for a promise" will implicitly be flattened to a promise for the inner result. Your "wrong solution" code simply works as is.

Source Show
◀ Wstecz