1

I am trying to wrap my post/get/put/delete calls so that any time they are called, if they fail they will check for expired token, and try again if that is the reason for failure, otherwise just resolve the response/error. Trying to avoid duplicating code four times, but I'm unsure how to resolve from a non-anonymous callback.

   factory.post = function (url, data, config) {
        var deferred = $q.defer();
        $http.post(url, data, config).then(factory.success, factory.fail);

        return deferred.promise;
     }

    factory.success = function (rsp) {
        if (rsp) {
           //how to resolve parent's promise from from here
        }
     }

Alternative is to duplicate this 4 times:

.then(function (rsp) {
           factory.success(rsp, deferred);
        }, function (err) {
           factory.fail(err, deferred);
        });
R. Richards
  • 24,603
  • 10
  • 64
  • 64
Nathan
  • 229
  • 5
  • 14
  • 2
    You should avoid the [$q defer Anti-pattern](https://stackoverflow.com/questions/30750207/is-this-a-deferred-antipattern). For more information, see [You're Missing the Point of Promises](https://blog.domenic.me/youre-missing-the-point-of-promises/). – georgeawg Mar 11 '18 at 06:41
  • This is classic AngularJS trouble. They wouldn't call it antipattern for nothing. Possible duplicate of https://stackoverflow.com/questions/30750207/is-this-a-deferred-antipattern – Estus Flask Mar 11 '18 at 12:24
  • Using q here adds value. Since I will be executing some shared code in the then before resolving. If I were to return the initial promise (return $http.post) then I would have to implement the then and fail in every factory that calls this. This is a wrapper for get/post/delete/put – Nathan Mar 11 '18 at 15:02

2 Answers2

0

One solution might be using bind function.

function sum(a){
  return a + this.b;
}

function callFn(cb){
  return cb(1);
}

function wrapper(b){
  var extra = {b: b};
  return callFn(sum.bind(extra));
} 

console.log(wrapper(5));
console.log(wrapper(-5));
console.log(wrapper(50));

For your solution check bellow example

factory.post = function (url, data, config) {
    var deferred = $q.defer();
    $http.post(url, data, config).then(factory.success.bind({deferred: deferred}), factory.fail.bind({deferred: deferred}));

    return deferred.promise;
 }

factory.success = function (rsp) {
    if (rsp) {
      this.deferred.resolve(rsp);
       //how to resolve parent's promise from from here
    }else {
      //retry or reject here
    }
 }
atiq1589
  • 2,227
  • 1
  • 16
  • 24
  • Learn something new about JS everyday. Could this lead to concurrency issues? If get/post calls were made before one returned, would the binding override the factory's function binding or are they instanced? – Nathan Mar 11 '18 at 05:33
  • Thats a very good point. I am also thinking about it while writing the solution. as object passing is pass by reference so it could be. One solution might be passing new object everytime. So it will create in reference while passing. Unfortunately I could not test now. If you could test it and provide the outcome it will be helpful for the community. – atiq1589 Mar 11 '18 at 05:47
  • I will test it later tonight and post response for anyone else who may want to know. – Nathan Mar 11 '18 at 15:03
0

From what I understand, you just want to resolve the deferred object on success and retry on error in case of expired token. Also you probably want to keep a count of number of retries. If so,

Edit - Seems I misunderstood the question. The answer suggested by Atiq should work, or if you are using any functional JS libraries like underscore or Ramdajs, you could use curry function. Using curry function, you can pass some parameters to the function and the function will get executed only after all the parameters are passed. I have modified the code snippet to use curry function from underscorejs.

factory.post = function (url, data, config) {
    var deferred = $q.defer();
    $http.post(url, data, 
    config).then(_.curry(factory.success(deferred)), 
    _.curry(factory.fail(deferred));

    return deferred.promise;
 }
 factory.success = function (deferred, rsp) {
        if (rsp) {
           //handle resp
           deferred.resolve(rsp);
        }
     }

    factory.fail = function(deferred, err){
        //handle retry
            deferred.reject(err);
    }
dilip
  • 51
  • 5
  • The problem is you have to write factory.success & factory.fail for every action (GET/POST/PUT/DELETE... ). The op try to minimize 4 lines but your solution increased LOC. – atiq1589 Mar 11 '18 at 05:51
  • seems I misunderstood the question. I have modified the code to use curry function, which is ideal for situations like this.. – dilip Mar 11 '18 at 06:25