在 angularjs 中处理延迟结果

working with deferred results in angularjs

我似乎无法理解 AngularJS 中 'promise' 的概念,我正在使用 restangular 库通过 REST 获取资源,但是我总是得到空结果。这是代码

.service('CareersService', [ 'Restangular', '$sce', function(Restangular, $sce){
            var vacancies = [];
            var result;
          this.getVacancies = function() {
                Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
                job_posts.forEach(function(job_post){
                    vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
                })  
            })
            return vacancies;
          }
            this.getVacancy = function(job_id){
                    Restangular.one('job_posts',job_id).get().then(function(job_post){
                    result = _.pick(job_post, 'title','min_experience','max_experience','location','employment_type','description');
                    var safe_description = $sce.trustAsHtml(result.description);
                        var emp_type = _.capitalize(result.employment_type);
                        _.set(result, 'description', safe_description);
                        _.set(result, 'employment_type', emp_type);
            });
              return result;            
            }
    }]).controller('DetailsCtrl', ['$scope' ,'$stateParams', 'CareersService' ,function($scope, $stateParams, CareersService) {

          $scope.data.vacancy = { title: 'Loading ...', contents: ''    };

          $scope.data.vacancy = CareersService.getVacancy($stateParams.job_id);

}])

然后在视图中

<div class="container">
    <a ui-sref="careers" class="btn btn-primary">Show All</a>
    <div class="row">
        <h2>{{ data.vacancy.title }}</h2>
        <p>{{ data.vacancy.min_experience }}</p>
        <p>{{ data.vacancy.max_experience }}</p>
        <p>{{ data.vacancy.location }}</p>
        <p>{{ data.vacancy.employment_type }}</p>
        <p ng-bind-html="data.vacancy.description"></p>     
    </div>
</div>

我是不是在使用 promises 的过程中遗漏了什么?


更新

这是更新后的代码,感谢我在这里得到的所有帮助,

          this.getVacancies = function() {
                Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
                job_posts.forEach(function(job_post){
                    vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
                })
                return vacancies;   
            })
          }
         this.getVacancy = function(job_id){
                    Restangular.one('job_posts',job_id).get().then(function(job_post){
                    vacancy = _.pick(job_post, 'title','min_experience','max_experience','location','employment_type','description');
                    ...
                    return vacancy;
            });
            }
}])

在控制器中

  CareersService.getVacancy($stateParams.job_id).then(function (vacancy){
            $scope.data.vacancy = vacancy;
          });

  CareersService.getVacancies().then(function (vacancies){
        $scope.data.vacancies = vacancies;
  });

我现在收到错误

Cannot read property 'then' of undefined

在第

CareersService.getVacancies().then(function(vacancies) {

Restangular 通过 http 进行 API 调用,一旦它进行调用,它就会成为 return 的底层承诺对象。在它的 .then 函数中你可以得到 API 响应的数据。

所以你在这里进行异步调用并考虑它以同步方式发生,就像你可以看到你有 returned result/vacancies array from Restangular 调用,这样 result/vacancies 总是空的。

在这种情况下,您应该 return 来自服务方法的承诺。 return 来自 promise 的适当格式化数据,以便您也可以 chain 在控制器中实现该 promise(通过检索数据)。

服务

this.getVacancies = function() {
  //returned Restangular promise
  return Restangular.all('job_posts').getList({
    active: 'true'
  }).then(function(job_posts) {
    job_posts.forEach(function(job_post) {
      vacancies.push(_.pick(job_post, ['id', 'title', 'min_experience', 'max_experience', 'location']));
    });
    //return calculated result
    return vacancies;
  })
}
this.getVacancy = function(job_id) {
  //returned Restangular promise
  return Restangular.one('job_posts', job_id).get().then(function(job_post) {
    result = _.pick(job_post, 'title', 'min_experience', 'max_experience', 'location', 'employment_type', 'description');
    var safe_description = $sce.trustAsHtml(result.description);
    var emp_type = _.capitalize(result.employment_type);
    _.set(result, 'description', safe_description);
    _.set(result, 'employment_type', emp_type);
    //returned result to chain promise
    return result;
  });
}

正如我现在所说的,您可以通过在服务方法调用上使用 .then 函数来轻松地在控制器内部链接承诺。

CareersService.getVacancy($stateParams.job_id).then(function(result){
    $scope.data.vacancy = result;
});

更新

没有 .then 的语法也可以,但您需要在方法调用后添加 .$object 对其进行小的改动。

$scope.data.vacancy = CareersService.getVacancy($stateParams.job_id).$object;

$object 是 属性 由 Restangular 在 promise 对象中添加的。在进行 API 调用时,它会将 $scope.data.vacancy 值设为空白数组 ([]),一旦服务器响应响应,它就会用服务器收到的响应填充该对象。在幕后,它只更新 $object 属性 的值,它会自动更新 $scope.data.vacancy 值。

ngResource$resource 中有相同的行为。


我还想记下,当您链接 promise 时,您必须显式处理错误情况。而在当前代码中,您还没有处理这种失败情况。因此,我建议您也这样做,方法是在 Restangular REST API 调用中添加 error 函数。并使用 $q.reject('My error data, this can be object as well').

您正在进行异步调用以获取结果。所以你需要一个回调或 promise 来处理这个。代码更改最少的一种选择是使服务成为 return 承诺,并在控制器中通过 then

获得结果
.service('CareersService', ['Restangular', '$sce', function(Restangular, $sce) {
  var vacancies = [];
  var result;
  this.getVacancies = function() {
    Restangular.all('job_posts').getList({
      active: 'true'
    }).then(function(job_posts) {
      job_posts.forEach(function(job_post) {
        vacancies.push(_.pick(job_post, ['id', 'title', 'min_experience', 'max_experience', 'location']));
      })
    })
    return vacancies;
  }
  this.getVacancy = function(job_id) {
    return Restangular.one('job_posts', job_id).get().then(function(job_post) {
      result = _.pick(job_post, 'title', 'min_experience', 'max_experience', 'location', 'employment_type', 'description');
      var safe_description = $sce.trustAsHtml(result.description);
      var emp_type = _.capitalize(result.employment_type);
      _.set(result, 'description', safe_description);
      _.set(result, 'employment_type', emp_type);
      return result;
    });
  }
}]).controller('DetailsCtrl', ['$scope', '$stateParams', 'CareersService', function($scope, $stateParams, CareersService) {

  $scope.data.vacancy = {
    title: 'Loading ...',
    contents: ''
  };

  CareersService.getVacancy($stateParams.job_id).then(function(result) {
    $scope.data.vacancy = result;
  });

}])

你在外面做 return 然后试着把它移到里面然后函数

this.getVacancies = function() {

            Restangular.all('job_posts').getList({active: 'true'}).then(function(job_posts){
            job_posts.forEach(function(job_post){
                vacancies.push(_.pick(job_post,['id','title','min_experience','max_experience','location']));
            })  
            return vacancies;
        })           
      }