【问题标题】:Using AngularJS $q promises in helper factory [duplicate]在辅助工厂中使用 AngularJS $q 承诺 [重复]
【发布时间】:2019-07-12 11:47:24
【问题描述】:

我正试图绕开 AngularJS 中何时使用 $q。到目前为止,我一直在我的所有服务中使用它,所以当我调用我们的 Web API 时,它运行良好。我要做的是减少对 API 的所有调用,因为我在多个地方需要相同的数据,而且每次需要数据时我都在 ping API。

现在我有一个服务,它获取数据和一个帮助文件来帮助处理与数据相关的事情。

我想要的是使用这个帮助器 factory 来保存每个使用它的机构所需的数据。

如果在 AngularJS 运行时数据还没有返回给我,我很难从帮助文件中分配数据的值。

这是我目前所拥有的。

(function() {
  var app = angular.module("Test", []);
  app.service("githubService", function($http, $q) {
    var deferred = $q.defer();

    this.getAccount = function() {
      return $http.get('https://api.github.com/users/JonDoe');
    };
  });

  app.factory("githubHelper", ["githubService", function(githubService) {
    _gitHubInfo = {};

    githubService.getAccount().then(function(data) {
      _gitHubInfo = data;
    });

    return {
      gitHubInfo: _gitHubInfo
    };
  }]);

  app.controller("Dummy", ["$scope", "githubHelper", "githubService", function($scope, githubHelper, githubService) {
    
    // How do I make it work this way?
    $scope.value = githubHelper.gitHubInfo;
    
    // This is what I'm using now
    githubService.getAccount().then(function(data) {
      $scope.value2 = data;
    });
  }]);
})();
.box {
  border: 1px red solid;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="Test">
  <div ng-controller="Dummy">
    <div class="box">
      {{value}}
    </div>
    <br />
    <div class="box">
      {{value2}}
    </div>
  </div>
</div>

我想要做的只是从Dummy 控制器中删除githubService 依赖项,并且只将它存在于githubHelper 工厂中,这样我就可以从我的所有控制器中删除对githubService 的依赖项,而是使用gitHubHelper.

我需要在Dummy 控制器中更改什么以使$scope.value 成为从githubService.getAccount() 返回的数据?

【问题讨论】:

  • 避免使用deferred Anti-Pattern。
  • 事实上,你是双重反模式,因为你返回 $http 承诺,然后从解析器返回延迟承诺......它就足够了调用 http.get,然后立即返回deferred.promise。反模式,是的,但比这更让人头疼
  • 更新到现在只返回来自$http.get的承诺。

标签: javascript angularjs asynchronous deferred


【解决方案1】:

我的项目中有这样的东西:

app.factory("githubHelper", ["githubService", function(githubService) {
    var promise = null;

    function getInfo() {
      if (!promise) {
        promise = githubService.getAccount();
      }
      return promise;
    }

    return {
      getInfo: getInfo
    };
  }]);

githubHelper.getInfo().then(function(data) {})
githubHelper.getInfo().then(function(data) {})
githubHelper.getInfo().then(function(data) {})
...

【讨论】:

  • 你为什么不直接return githubService.getAccount();。为什么要保存 promise 对象?
  • @Jimenemex 因为 OP 想要缓存它而不是发出另一个请求。这是正确的解决方案。
  • @Bergi 对不起,但是如果我只是删除函数 getInfo() 并在帮助程序 return { getInfo: githubService.getAccount} 中公开服务函数有什么不同。然后我可以调用该函数并在控制器中有.then? githubHelper.getInfo().then(... 那时我不需要var promise。
  • @Jimenemex 用法与此答案中的代码完全相同。不同之处在于多次调用getInfo 都会导致相同的结果,只发出一个请求(从设置promise 的第一个调用开始)。
  • @Jimenemex 哦,我才意识到你是 OP。这不是你想要的吗?
【解决方案2】:

你几乎在那里。问题是 githubinfo 在您访问它之前不会被填充。你应该做的几乎完全是(见我上面的评论)你正在做的githubservice.getaccount,而是githubhelper.getaccount。将githubinfo设置为$q.deferred,从getaccount返回githubinfo.promise,解析then中的promise

更新:现在有更多代码! (现在我不在手机上:-D)

(function() {
  var app = angular.module("Test", []);
  app.service("githubService", function($http, $q) {
    this.getAccount = function() {
      return $http.get('https://api.github.com/users/JonDoe');
    };
  });

  app.factory("githubHelper", ["githubService", function(githubService) {
    return {
      gitHubInfo: $q(function(resolve, reject) {
        githubService.getAccount().then(function(data) {
          resolve(data);
        }, reject);
      }
    };
  }]);

  app.controller("Dummy", ["$scope", "githubHelper",
    function($scope, githubHelper) {

      githubHelper.gitHubInfo.then(function(data) {
        $scope.value = data;
      });

  }]);
})();

现在,按原样编写,出于多种原因,我永远不会批准它作为 PR(代码清晰回复:return { someProp: function() { /* a bunch of code */} } .... 应不惜一切代价避免使用 $scope... 如上所述, $cacheFactory 可以而且应该处理这个问题),但是您应该能够大致了解这些方面的事情可以如何工作

【讨论】:

  • 我不需要在服务自己的方法中包围对githubService.getAccount() 的调用,并从那里返回promise,同时在return {} 中公开新方法吗?难道我不需要在控制器中有.then 回调吗?
  • 抱歉,您的代码略有误读。您将返回的githubinfo 属性设置为githubinfo.promise 并在getAccount 的then 中解析它。您还应该考虑将拒绝回调设置为githubinfo.reject,但这是另一回事。是的,你必须在你的控制器中有一个`.then,但这是同步开发的本质。您唯一的其他选择是在路由解析器中启动请求,但我认为您不希望这样做。问题:为什么不使用 cacheFactory?
  • @Bergi 是的,“避免”但不是“从不使用”......在某些情况下,这是一种完全有效的方法(在这种情况下不一定),如果你注意到,我做到了“正确”在我更新的代码示例中:-) 非常具体地说,如果他想在请求数据的一些 n 次之后(或在一些 t 时间之后)生成一个新的承诺并且需要替换 @987654345 是有效的@ 带有要解决的新承诺。是的,也有一些方法可以避免它,但是 IMO 那些往往是应该完全避免的反模式反模式
  • 不,这确实是一种“从不使用”模式,在这种情况下也是如此。 $q(function(resolve, reject) { githubService.getAccount().then(resolve, reject); 绝对应该避免。你也不需要$q(…) 来替换一个不同的承诺。
猜你喜欢
  • 2017-01-30
  • 2016-03-18
  • 2013-09-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多