Javascript 如何让控制器等待 Angular 服务的承诺解决

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25899717/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 22:03:00  来源:igfitidea点击:

How to make controller wait for promise to resolve from angular service

javascriptajaxangularjspromiseangularjs-service

提问by Axschech

I have a service that is making an AJAX request to the backend

我有一个服务向后端发出 AJAX 请求

Service:

服务:

    function GetCompaniesService(options)
    {
        this.url = '/company';
        this.Companies = undefined;
        this.CompaniesPromise = $http.get(this.url);

    }

Controller:

控制器:

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(data){
   $scope.Companies = data;
});

I want my service to handle the ".then" function instead of having to handle it in my controller, and I want to be able to have my controller act on that data FROM the service, after the promise inside the service has been resolved.

我希望我的服务处理“.then”函数,而不是必须在我的控制器中处理它,并且我希望能够让我的控制器在服务内部的承诺得到解决后对来自服务的数据采取行动。

Basically, I want to be able to access the data like so:

基本上,我希望能够像这样访问数据:

var CompaniesOb = new GetCompanies();
$scope.Companies = CompaniesOb.Companies;

With the resolution of the promise being handled inside of the service itself.

在服务本身内部处理承诺的解决方案。

Is this possible? Or is the only way that I can access that promise's resolution is from outside the service?

这可能吗?或者我可以从服务外部访问该承诺的解决方案的唯一方法是什么?

回答by M.K. Safi

If all you want is to handle the response of $httpin the service itself, you can add a thenfunction to the service where you do more processing then returnfrom that thenfunction, like this:

如果您只想处理$http服务本身的响应,您可以then向服务添加一个函数,然后return从该then函数执行更多处理,如下所示:

function GetCompaniesService(options) {
  this.url = '/company';
  this.Companies = undefined;
  this.CompaniesPromise = $http.get(this.url).then(function(response) {
    /* handle response then */
    return response
  })
}

But you'll still have use a promise in the controller, but what you get back will have already been handled in the service.

但是你仍然会在控制器中使用一个承诺,但是你得到的东西已经在服务中被处理了。

var CompaniesOb = new GetCompanies();
CompaniesOb.CompaniesPromise.then(function(dataAlreadyHandledInService) {
  $scope.Companies = dataAlreadyHandledInService;
});

回答by Piou

There is no problem to achieve that!

实现这一目标没有问题!

The main thing you have to keep in mind is that you have to keep the same object reference (and in javascript arrays are objects) in your service.

您必须记住的主要事情是您必须在您的服务中保持相同的对象引用(并且在 javascript 数组中是对象)。

here is our simple HTML:

这是我们的简单 HTML:

<div ng-controller = "companiesCtrl"> 
  <ul ng-repeat="company in companies">
     <li>{{company}}</li>
  </ul>
</div>

Here is our service implementation:

这是我们的服务实现:

serviceDataCaching.service('companiesSrv', ['$timeout', function($timeout){
  var self = this;

  var httpResult = [
    'company 1',
    'company 2',
    'company 3'
  ];

  this.companies = ['preloaded company'];
  this.getCompanies = function() {
    // we simulate an async operation
    return $timeout(function(){
      // keep the array object reference!!
      self.companies.splice(0, self.companies.length);

      // if you use the following code:
      // self.companies = [];
      // the controller will loose the reference to the array object as we are creating an new one
      // as a result it will no longer get the changes made here!
      for(var i=0; i< httpResult.length; i++){
        self.companies.push(httpResult[i]);
      }
      return self.companies;
    }, 3000);                    
}}]);   

And finally the controller as you wanted it:

最后是你想要的控制器:

serviceDataCaching.controller('companiesCtrl', function ($scope, companiesSrv) {
  $scope.companies = companiesSrv.companies;
  companiesSrv.getCompanies();
});

Explanations

说明

As said above, the trick is to keep the reference between the service and the controller. Once you respect this, you can totally bind your controller scope on a public property of your service.

如上所述,诀窍是保持服务和控制器之间的引用。一旦您尊重这一点,您就可以将您的控制器范围完全绑定到您服务的公共属性上。

Here a fiddle that wraps it up.

这是一个把它包起来的小提琴。

In the comments of the code you can try uncomment the piece that does not work and you will see how the controller is loosing the reference. In fact the controller will keep having a reference to the old array while the service will change the new one.

在代码的注释中,您可以尝试取消对不起作用的部分的注释,您将看到控制器如何丢失引用。事实上,控制器将保持对旧数组的引用,而服务将更改新数组。

One last important thing: keep in mind that the $timeout is triggering a $apply() on the rootSCope. This is why our controller scope is refreshing 'alone'. Without it, and if you try to replace it with a normal setTimeout() you will see that the controller is not updating the company list. To work around this you can:

最后一件重要的事情:请记住,$timeout 正在触发 rootSCOpe 上的 $apply()。这就是我们的控制器范围“单独”刷新的原因。没有它,如果您尝试用普通的 setTimeout() 替换它,您将看到控制器没有更新公司列表。要解决此问题,您可以:

  • don't do anything if your data is fetched with $http as it calls a $apply on success
  • wrap you result in a $timeout(..., 0);
  • inject $rootSCope in the service and call $apply() on it when the asynchronous operation is done
  • in the controller add a $scope.$apply() on the getCompanies() promise success
  • 如果您的数据是使用 $http 获取的,则不要做任何事情,因为它在成功时调用 $apply
  • 包裹你的结果是 $timeout(..., 0);
  • 在服务中注入 $rootSCOpe 并在异步操作完成时对其调用 $apply()
  • 在控制器中,在 getCompanies() 承诺成功上添加 $scope.$apply()

Hope this helps!

希望这可以帮助!

回答by Scott

You can pass the $scope into GetCompanies and set $scope.Companies to the data in the service

您可以将 $scope 传递给 GetCompanies 并将 $scope.Companies 设置为服务中的数据

 function GetCompaniesService(options,scope)
 {
    this.url = '/company';
    this.Companies = undefined;
    this.CompaniesPromise = $http.get(this.url).then(function(res) {
      scope.Companies = res;
    });

 }

You have to be careful about the order in which you then use the data. That's kind of the reason behind a promise to begin with.

您必须注意随后使用数据的顺序。这就是最初承诺背后的原因。