【问题标题】:Using the $q implementation in Angular how can I loop a promise until success?在 Angular 中使用 $q 实现如何循环一个承诺直到成功?
【发布时间】:2014-02-04 11:29:33
【问题描述】:

根据我最近的其他问题,我正在尝试将针对移动设备(不稳定连接)的 Angular 应用程序的数据持久化到服务器,因此它应该继续尝试请求直到成功。

如何使用 Promise 做到这一点?

目前我有:

服务:

 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 循环。如何强制承诺重复直到成功回调?

【问题讨论】:

  • 您的代码中没有循环。
  • @Adam - 你是因为我错误地将 setTimeout 描述为循环,还是我没有抓住重点?
  • @GruffBunny - 该问题中的代码不起作用,我是否认为我只需要调用该函数一次即可启动它?
  • @Anonymous:可能,请edit您的问题来修正这个术语。那个setTimeout在那里用的是什么?

标签: javascript angularjs promise q


【解决方案1】:

这是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;
    }

【讨论】:

    【解决方案2】:

    正如您对$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);
         }
      }
    });
    

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

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

    注意:可能需要更多的逻辑来确保它只重试正确的错误类型。即它不是真正的 404,实际上是浏览器的责任,例如。

    【讨论】:

      【解决方案3】:

      它应该继续尝试请求直到成功。我如何使用 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");
      });
      

      【讨论】:

        【解决方案4】:

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

        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);
                     })
        

        【讨论】:

          【解决方案5】:

          嗯。尽管有针对这种特殊情况的 HTTP 拦截器,但使用 Promise 而不是回调的优点之一是您可以使用高阶函数

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

          例如,一个函数接受一个promise making function并产生一个新的promise Making function,该函数重复地重试另一个promise,直到达到最大值。

          例如:(这是使用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);
                  });
          }());
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-06-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-11-27
            相关资源
            最近更新 更多