带有promise.then()的Angular Infinite Digest循环

时间:2017-07-05 14:19:11

标签: angularjs promise

问题:在控制器方法中使用this.promise.then(function(){})函数似乎创建了$ rootScope:infDig循环,尽管该方法的返回值不会改变。值得注意的是,当删除.then()行时,无限摘要循环消失了(参见代码)。

代码: 视图中的{{ getSelectedProjects() }}和附加控制器中的以下方法

var vm = this;
vm.getSelectedProjects = function() {
  if (angular.isUndefined(vm.hello)) {
    vm.hello = "hello"
    vm.promise = $q.when("hello i am a resolved promise")
  }
  vm.promise //In my original code, this promise is returned by a service (MyService.getPromise()). The resolved value of the returned promise may change which is why the service needs to be called at each digest cycle.
  .then(function (ans) {
    vm.hello = ans;
  }); //remove .then(...) function and there is no infinite digest loop anymore
  return vm.hello;
}

问题:为什么这会创建一个无限的摘要循环,尽管返回的值不会改变?我怎么能避免它(我非常需要那个承诺的结果)?。

我摇摇欲坠的想法:我想这与事实有关.then()每次都会返回一个新的承诺。文档没有说明它,但如果该promise每次都附加到控制器对象(vm),这可能被视为控制器的状态更改并触发新的摘要循环,重新计算vm.getSelectedProjects()并再次向控制器等附加新的承诺。

修改 我的假设是“不稳定的想法”#34;似乎不完全正确或完整,因为此代码仍然存在问题:

vm.getSelectedProjects = function() {
  if (angular.isUndefined(vm.hello)) {
    vm.hello = "hello"
  }
  $q.when("hello i am a resolved promise")//this object is not attached to the controller object
  .then(function () {});
  return vm.hello;
}

1 个答案:

答案 0 :(得分:0)

基于遇到的问题和评论,我们为在项目中使用promises创建了编码指南。发现分享它很有意思,因为我没有在任何地方看到这个记录。

编码指南:使用承诺

使用承诺

Promise是令人惊奇的对象,但错误的用法会让人头疼。因此,我们有一些指导原则:

  • 请勿在观看中使用承诺
  • 不要在控制器中使用promises,除了事件处理程序调用的函数(click,init等),控制器构造函数(=只执行一次)或动画
  • 如果在视图中使用了promise结果,则模型(或管理器)应该包含一个默认为空的属性,并在解析promise时设置为已解析的值。控制器只返回该值。

实施

这是应该在模型,管理器或其他服务中的结构,尽可能接近数据源。不在控制器中:

  var myApiMethod = function() {
    //We call "caching" the fact that this method is executed only if $scope.item is undefined.
    If (angular.isUndefined($scope.item) { 
      $scope.item = {}
      $http(xxx).then(function(value){
        $scope.item = value;
      });
      return $scope.item
    }
  }

然后控制器只返回myApiMethod的答案。您可能需要检查'myService.myApiMethod()!= {}'。

在服务中,我们可以定义缓存行为(例如:每2小时从后端检索数据,在某个事件中清除缓存等)。只要有一个单一的事实来源(来自本案例中的服务,而不是来自控制器)关于要返回的对象,您可以轻松地玩这个。

论证

*为什么不在视图或表达式评估调用的控制器方法中使用promises(例如{{vm.getMyApiMethodResult()}} *:

  • 在视图中包含承诺是在Angular 1.x
  • 中已停用的功能
  • 如果myApiMethod没有缓存放在控制器中,然后用于表达式评估(例如{{myCtrl.getMyApiMethod()}}),它将创建一个无限的摘要周期,因为promise的解析将触发一个新的摘要周期,它将再次调用vm.getMyApiMethod()函数等。(this SO answer中的更多信息)
  • 为什么不将myApiMethod放入控制器中进行缓存(如上面的实现),以确保myApiMethod只被调用一次并避免无限的摘要循环?原因有三:

    1. 如果myApiMethod()的响应更改为新对象,则不会更新$ scope.item。
    2. 这个的另一个缺点(我们过去经常使用,你可能仍会在代码中看到错误的实现)是因为我们需要复制这个非常繁重的代码结构(它可能会成为调用myApiMethod()
    3. 的所有控制器中涉及多个承诺时要复杂得多
    4. 如果你在控制器中“缓存”,你可能最终得到不同的控制器返回不同的答案,尽管他们都从同一服务'myApiMethod'返回答案,但是在不同的时刻。

请记住:本指南仅适用于表达式评估中调用的控制器函数。您可能需要在事件处理程序调用的控制器函数中使用promise(单击等),这很好,因为在每个角度的摘要周期都不会调用这些函数。