在Angular中使用$ q实现我如何循环一个promise直到成功?

时间:2014-01-15 19:49:32

标签: javascript angularjs promise q

根据我最近的其他问题,我试图将数据保存到服务器以获取针对移动设备(不稳定连接)的角度应用程序,因此它应该继续尝试请求直到成功。

我怎么能用承诺做到这一点?

目前我已经:

服务

 this.addObject = function addObject(object) {
        var deferred = $q.defer();

      var httpConfig = {
        method: 'POST',
        url: 'http://api.foo.com/bar',
        data: object
      }

      setTimeout(function() {
          $http(httpConfig).
              success(function(data, status) {
                  deferred.resolve('Woohoo!');
              }).
              error(function(data, status) {
                  deferred.reject('Couldnt reach server this time...');
              });
      }, 3000);

        return deferred.promise;
    }

控制器:

myService.addObject(myObject)
            .then(function(message) {
                console.log(message);
            }, function(message) {
                console.log(message);
            });

我无法删除拒绝回调,因为代码似乎在没有它的情况下执行,但是一旦调用了拒绝,它就会破坏setTimeout循环。在成功回调之前,我如何强制承诺重复?

5 个答案:

答案 0 :(得分:3)

这是AngularJS service retry when promise is rejected

的答案的正确形式
 this.addObject = function addObject(object) {
    var counter = 0;
      var deferred = $q.defer();

      var httpConfig = {
        method: 'POST',
        url: 'http://api.foo.com/bar',
        data: object
      }

      var doRequest = function() {
        counter++;
        var self = this,args = arguments;
      $http(httpConfig).
          success(function(data, status) {
              deferred.resolve('Woohoo!');
          }).
          error(function(data, status) {
            //just fail already, it's not working
            if(counter > 5) {
                return deferred.reject('Couldnt reach server this time...');
            }

            //this will re-call doRequest();
            args.callee.apply(self);
          });

      }

      doRequest();

        return deferred.promise;
    }

答案 1 :(得分:0)

当您使用$http时,http拦截器可以执行此操作。如果您希望http请求无限循环,直到它返回成功:

app.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('RetryInterceptor');
  }]);

app.factory('RetryInterceptor', function($timeout, $injector) {
  return {
   'responseError': function(rejection) {
      // Manual inject to work around circular dependency issue
      var $http = $injector.get('$http');
      return $timeout(function() {
        return $http(rejection.config);
      },3000);
     }
  }
});

这是因为对$ http的初始调用在(在这种情况下)所有的responseError拦截器都已解决后才会解析。

您可以在http://plnkr.co/edit/QAa9oIK4lTM6YwccEed3?p=preview看到这一点(在控制台日志中查看每3秒发生一次失败的请求。)

注意:可能需要更多逻辑来确保它只在正确的错误上重试。例如,它不是真正的404,实际上浏览器应该受到指责。

答案 2 :(得分:0)

  

它应该继续尝试请求,直到成功。我怎么能用承诺做到这一点?

通过“递归”在错误处理程序中再次调用该函数,以便您使用下一次尝试的结果解析promise。

this.addObject = function addObject(object) {
  var httpConfig = {
    method: 'POST',
    url: 'http://api.foo.com/bar',
    data: object
  }

  return $http(httpConfig).then(function(data, status) {
    return 'Woohoo!';
  }, function(data, status) {
    throw 'Couldnt reach server this time...';
  });
}

this.addObjectForever = function addObject(object) {
  var that = this;
  return this.addObject(object).then(null, function(err) {
    console.log("Failed this time");
    return $timeout(3000).then(function() {
      console.log("Trying again now");
      return that.addObjectForever(object);
    });
  });
};

instance.addObjectForever(obj).done(function() {
  console.log("It eventually worked");
});

答案 3 :(得分:0)

这里非常拥挤:) 我的解决方案:

angular.module('app', [])
             .service('Service',function($q,$http) {

                this.addObject = function(object) {
                     var httpConfig = {
                        method: 'POST',
                        url: 'http://api.foo.com/bar',
                        data: object
                    }
                    var deferred = $q.defer();
                    $http(httpConfig)
                            .success(function(data, status) {
                                deferred.resolve('Woohoo!');
                            })
                            .error(function(data, status) {
                                deferred.reject('Couldnt reach server this time...');
                            });

                   return deferred.promise;

                };

             })
             .controller('MainCtrl',function($scope,$interval,Service){
                 /*Service.addObject({})
                    .then(function(message) {
                        console.log(message);
                    }, function(message) {
                        console.log(message);
                    });*/
                $interval(function(){
                    Service.addObject().then(function(message) {
                        console.log(message);
                    }, function(message) {
                        console.log(message);
                    });
                },1000);
             })

答案 4 :(得分:0)

嗯。即使有针对此特定情况的HTTP拦截器,使用promises而不是回调的优点之一是您可以使用更高阶函数

// both p and q are functions that create a promise
p = makeSomePromise(options)
q = repeat(10, p)

也就是说,例如,一个函数采用承诺生成函数并生成一个新的承诺生成函数,重复重试另一个函数,直到最大。 / p>

例如:(这是针对使用kew的nodeJS,但你应该明白这一点)

var Q = require('kew');

function retryPromise(n, mkPromise) {
    if(n > 0)
        return mkPromise()
            .fail(function(){
                console.log('failed promise, retrying maximum ' + n + ' more times');
                return retryPromise(n - 1, mkPromise);
            });

    return mkPromise();
}


(function(){
    var mkOp = function(){
        if(Math.random() > .1)
            return Q
                .fcall(function(){return 1;})
                .then(function(){throw Error('aah');});

        return Q.fcall(function(){return 'yay'});
    };

    retryPromise(10, mkOp)
        .then(function(data){
            console.log(data);
        })
        .fail(function(err){
            console.log('failed', err);
        });
}());