0

Hi I am trying to implement Promise Chaining Using q-library in my angular project below are my 2 methods and the way they are chained.I have also posted method implementations to understand my problem. Now in pathUserDataFromAuthService method I would like to access the Etag value that i have set in the first Promise i.e getUserDataFromAuthServer using setEtag() method , The problem here is getEtag() method is called before the first promise data is resolved .

How would ensure that correct Etag value is coming up

Any help in this regard would be great

  getUserDataFromAuthServer(user)
            .then(pathUserDataFromAuthService(user))

and the method code is as below

    getUserDataFromAuthServer = function(user){
                    var defer=$q.defer();
                    $http.get(autherizationURL+'/'+'api/users/'+user.username)
                   .then(function(response){
                        var ETag =response.headers().etag;
                        console.log("etag"+ETag)
                        setEtag(ETag)
                        console.log("Etag Set changed");
                        defer.resolve(response.data);
                    },function(error){          
                          defer.reject(error.statusText);
                    });
                    return defer.promise;       
                };



            var pathUserDataFromAuthService = function (user){      
                var defer=$q.defer();
                var passwordtobesaved ={
                        "firstName": user.firstName,
                        "lastName": user.lastName
                    };
                var tag = getEtag();
            $http.put(autherizationURL+'/'+'api/users/'+user.username,passwordtobesaved,{
                    headers:{
                        "If-Match": tag
                    }
                }).then(function(response){

                    defer.resolve(response.data);
                },function(error){          
                      defer.reject(error.statusText);
                });
                return defer.promise;       
            };
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Raghu Chaitanya
  • 151
  • 2
  • 8
  • 1
    First of all, [avoid the deferred antipattern](http://stackoverflow.com/q/23803743/1048572)! – Bergi Mar 30 '16 at 18:32
  • 1
    You must pass a callback *function* to `then`, not a promise (the result of calling a function) – Bergi Mar 30 '16 at 18:33

2 Answers2

1

No need to use defer, this is an anti-pattern. You can simply pass on the promise by returning the http request.

Here's how it should be:

getUserDataFromAuthServer = function(user){
  return $http.get(autherizationURL+'/'+'api/users/'+user.username)
      .then(function(response){
           var ETag =response.headers().etag;
           console.log("etag"+ETag)
           setEtag(ETag)
           console.log("Etag Set changed");
           return response.data;
        }
};

Same thing can be done in your second function, although I don't see why it's needed since you do nothing when it ends.

var pathUserDataFromAuthService = function (user){      
  var passwordtobesaved ={
    "firstName": user.firstName,
    "lastName": user.lastName
  };
  var tag = getEtag();
  return $http.put(
    autherizationURL + '/' + 'api/users/' + user.username, 
    passwordtobesaved, 
    { headers: { "If-Match": tag } }
  )
};
Asaf David
  • 3,167
  • 2
  • 22
  • 38
  • I am using the Etag from the first Request and while posting it to the server i need the Etag value so the server makes an Optimistic Lock on the Object – Raghu Chaitanya Mar 30 '16 at 18:42
  • I've added the second function as well – Asaf David Mar 30 '16 at 18:48
  • 1
    You'd have to use `throw error.statusText` though (even if rejecting with/throwing strings is in itself an antipattern) – Bergi Mar 30 '16 at 18:49
  • A small comment - better use camel case for variables. passwordtobesaved => passwordToBeSaved – Asaf David Mar 30 '16 at 18:49
  • @Bergi So just change the `return` to `throw` in the error function? – Asaf David Mar 30 '16 at 18:51
  • 1
    @AsafDavid: Yes, the `deferrerd.reject` the OP had is equivalent to a `throw` – Bergi Mar 30 '16 at 18:52
  • @Bergi what if I just remove the error function? Will he be able to catch the error at the same place he catches the promise? – Asaf David Mar 30 '16 at 18:52
  • 1
    @AsafDavid: If you omit the error handler, the resulting promise will be rejected with the same reason as the one `then` was called upon. It just bubbles through. – Bergi Mar 30 '16 at 18:53
0

As already pointed out, the main issue is that then() accepts function(s).

After that, the need to pass user makes things slightly awkward but nothing that javascript can't cope with.

A number of approaches are possible. Here are two.

1. Pass user and etag separately, with the help of .bind()

It's maybe not obvious how this works because the two parameters user and etag are passed by different mechanisms :

  • user: is "bound"
  • etag: is delivered by the promise chain to the function returned by .bind()
getUserDataFromAuthServer(user).then(pathUserDataFromAuthService.bind(null, user));

getUserDataFromAuthServer = function(user) {
    return $http.get(autherizationURL + '/api/users/' + user.username).then(function(response) {
        return response.headers().etag;
    });
};

var pathUserDataFromAuthService = function(user, etag) {
    return $http.put(autherizationURL + '/api/users/' + user.username, {
        "firstName": user.firstName,
        "lastName": user.lastName
    }, {
        headers: { "If-Match": etag }
    }).then(function(response) {
        return response.data;
    });
};

2. Deliver user and etag as properties of a single object

This is probably more intuitive. The promise chain delivers a single object to pathUserDataFromAuthService as data.

getUserDataFromAuthServer(user).then(pathUserDataFromAuthService);

getUserDataFromAuthServer = function(user) {
    return $http.get(autherizationURL + '/api/users/' + user.username).then(function(response) {
        return {
            user: user,
            etag: response.headers().etag
        };
    });
};

var pathUserDataFromAuthService = function(data) {
    return $http.put(autherizationURL + '/api/users/' + data.user.username, {
        "firstName": data.user.firstName,
        "lastName": data.user.lastName
    }, {
        headers: { "If-Match": data.etag }
    }).then(function(response) {
        return response.data;
    });
};

In both solutions setEtag() / getEtag() disappear on the assumption that they existed (in the question) solely as a mechanism for passing ETag. If necessary for other reasons (eg side-effects), they can be reintroduced without consequence (unless they throw).

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44