简体   繁体   English

JavaScript嵌套的承诺范围和AngularJS

[英]JavaScript nested promise scoping and AngularJS

I have a real problem with JavaScript promises that I've been trying to solve for the last few hours and I just can't seem to fix it. 我在JavaScript承诺中遇到了一个真正的问题,这是我在最近几个小时内一直在尝试解决的问题,但似乎无法解决。 My experience with promises is limited so I'm open to the idea that my approach is simply incorrect. 我对诺言的经验有限,因此我对自己的做法完全错误的想法持开放态度。

Right now I'm building an app that requires a two-step process: 现在,我正在构建一个需要两步过程的应用程序:

  1. Connect to an external PaaS service, which returns a promise 连接到外部PaaS服务,该服务将返回承诺
  2. Within that promise, retrieve some data 在该承诺范围内,检索一些数据

Here's a sample of a factory I created: 这是我创建的工厂的示例:

app.factory('serviceFactory', [
    function() {
        var getData = function getData() {
            service.connect(apiKey).then(function() {
                    service.getData('dataStore').then(function(result) {
                        // Retrieve data
                        return result;
                    }, errorFunction);
                },
                errorFunction);
        };
        return {
                getData: getData
        };
    }

]);

As you can see, there are nested promises here. 如您所见,这里有嵌套的承诺。 What's causing me problems is when I try to use the data from the most deeply-nested promise within an AngularJS view. 导致我出现问题的是,当我尝试使用AngularJS视图中最深层嵌套的诺言中的数据时。 Specifically, I want to use the data from that promise in an ng-repeat statement. 具体来说,我想在ng-repeat语句中使用该诺言中的数据。 But no matter what I try, it just won't show up. 但是,无论我尝试什么,它都不会显示。 I've attempted to assign data within the promise instead of returning, like so: 我试图在promise中分配数据而不是返回,就像这样:

service.getData('dataStore').then(function(result) {
    // Retrieve data
    // Assigned the enclosing scope's this to 'self'
    self.data = result;
}, errorFunction);

That doesn't work either. 那也不行。 I've tried a variety of other approaches, but I just can't seem to get that data to the view. 我尝试了多种其他方法,但似乎无法将这些数据显示在视图中。 There's no problem getting it to show up in a console.log(data) call, so I know the data is coming back correctly. 让它显示在console.log(data)调用中没有问题,所以我知道数据可以正确返回。 Does anyone have experience solving a problem like this? 有没有人有解决这样的问题的经验?

I would suggest that you'll try to avoid nested promises. 我建议您尽量避免嵌套承诺。 You can take a look at this blog post , which will let you see how you can avoid 'promise soup' and have promise chaining instead. 您可以看一下这篇博客文章 ,该文章将使您了解如何避免“无极汤”,而可以实现承诺链。

As for your question, I would recommend the following: 关于您的问题,我建议以下内容:

A quick solution will be to fix your problem. 一个快速的解决方案是解决您的问题。 You are returning the factory method wrong: 您返回错误的工厂方法:

app.factory('serviceFactory', [
    function() {
        var getData = function getData() {
            return service.connect(apiKey).then(function() {
                    service.getData('dataStore').then(function(result) {
                        // Retrieve data
                        return result;
                    }, errorFunction);
                },
                errorFunction);
        };//here you should close the 'getData method
        return {
            getData: getData
        };   
    }
]);

But, you can refactor your code to chain your promises. 但是,您可以重构代码以链接您的诺言。 Something like: 就像是:

app.factory('serviceFactory', [
    function() {
        var connect = function connect() {
            return service.connect(apiKey);
        };
        var getData = function getData(data) {
            return service.getData(data);
        };       
        return {
            getData: getData,
            connect: connect
        };
    }
]);

Now, you can do something like this: 现在,您可以执行以下操作:

serviceFactory.connect(apiKey)
     .then(serviceFactory.getData)
     .then(function(result){
           //use data here
      })

All of this should be tested - you can add a plunker or jsbin if you want a working solution... 所有这些都应该经过测试-如果您想要一个可行的解决方案,则可以添加一个插件或jsbin ...

EDIT 编辑

I think that you have another problem here. 我认为您在这里还有另一个问题。 You are mixing between serviceFactory and service . 您正在serviceFactoryservice之间混合service I'm not sure that I understand if this is the same service, or which is who. 我不确定我是否了解这是同一服务还是谁。 Can you provide a more detailed code or add plunker/jsbin etc. 您能否提供更详细的代码或添加plunker / jsbin等。

I've edited this answer, which I originally deleted because I didn't explain what I meant very clearly and it garnered some downvotes (without explanation, but that's my guess). 我已经编辑了这个答案,我最初删除了这个答案,因为我没有很清楚地解释我的意思,并且赢得了一些反对意见(没有解释,但这是我的猜测)。 Anyway, here is a more complete answer. 无论如何,这是一个更完整的答案。

I suspect that your problem is that whatever PaaS you are using has no awareness of Angular, and Angular likewise has no awareness of the PaaS. 我怀疑您的问题是您使用的任何PaaS都不了解Angular,而Angular同样也不了解PaaS。 You say in your question that the PaaS has methods that return promises, but if Angular is not aware of those promises, then, when the promises resolve, Angular does not know to update the DOM. 您在问题中说,PaaS具有返回承诺的方法,但是如果Angular不知道这些承诺,那么当承诺解决时,Angular不知道更新DOM。 Angular does this via the digest cycle which is where Angular checks everything that it is watching to see if it has changed. Angular通过摘要循环执行此操作,在该摘要循环中,Angular会检查其正在监视的所有内容以查看其是否已更改。 When using $q (or other Angular services like $http ), Angular knows to automatically kick off a digest cycle when they resolve. 当使用$q (或像其他角服务$http ),角知道自动揭开序幕消化周期时,他们解决。 It does not, however, kick off a digest cycle when promises created by other means resolve. 但是,通过其他方式创建的承诺无法解决时,它不会启动摘要循环。

This is what I think is happening in your code. 我认为这就是您的代码中正在发生的事情。 Your PaaS is giving you promises, which are resolving properly (you said you can see the results via console), but your HTML is not being updated. 您的PaaS给您的诺言已经很好地解决了(您说可以通过控制台查看结果),但是您的HTML尚未更新。

I modified the plunkr we were working on to demonstrate this in action. 我修改了我们正在研究的plunkr ,以演示其实际效果。 I created a mock PaaS (not knowing what you are using) that creates promises using jQuery and resolves them. 我创建了一个模拟PaaS(不知道您在使用什么),它使用jQuery创建了promise并解决了它们。 As you can see, when the promises resolve, the result is logged to the console, but the DOM is not resolved. 如您所见,当promise解析时,结果将记录到控制台,但DOM尚未解析。

angular.module("app",[])
  .value("mockPaaS", mockPaaS)
  .factory("dataFactory", function($q, mockPaaS){

    function getData(){
      return mockPaaS.connect()
        .then(mockPaaS.getData);
    }
    return {
      getData: getData
    }
  })
  .controller("DataController", function (dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
      });
  })
  .directive("myApp", function(){
      return {
        bindToController: true,
        controller: "DataController",
        controllerAs: 'myApp',
        template: "<div ng-repeat='i in myApp.dataArr'>{{i}}</div>"
    };
  });

I was originally suggesting that you could solve this problem by adding a $scope.$apply() after you capture the result of the promise. 我最初建议您可以在捕获诺言的结果后添加$scope.$apply()来解决此问题。 I've forked the Plunker and you can see here it does, in fact update the DOM. 我分叉了Plunker,您可以在这里看到它的确,实际上是在更新DOM。

  .controller("DataController", function ($scope, dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
        $scope.$apply();
      });
  })

There is, however, a more idiomatic solution. 但是,还有一个惯用的解决方案。 When you get a promise from outside angular that you need to use in Angular, you can wrap that promise using $q.when (an Angular aware promise), and when the external promise resolves, Angular should kick off it's digest cycle naturally. 当您从Angular中获得需要在Angular中使用的promise时可以使用$ q.when (Angular知道的Promise)包装该Promise ,并且当外部Promise解析时,Angular应该自然地开始它的摘要周期。

  .factory("dataFactory", function($q, mockPaaS){
    function getData(){
      return $q.when(mockPaaS.connect()
        .then(mockPaaS.getData));
    }
    return {
      getData: getData
    }
  })
  .controller("DataController", function (dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
      });
  })

Ben Nadel gives a nice explanation of this issue here . 本·纳德勒(Ben Nadel) 在这里对此问题做了很好的解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM