JavaScript嵌套的promise范围和AngularJS

时间:2015-05-26 00:11:26

标签: javascript angularjs promise angular-promise

我的JavaScript承诺存在一个真正的问题,我过去几个小时一直试图解决这个问题,而我似乎无法修复它。我对承诺的体验是有限的,所以我对我的方法完全不正确的想法持开放态度。

现在我正在构建一个需要两个步骤的应用程序:

  1. 连接到外部PaaS服务,该服务返回承诺
  2. 在该承诺中,检索一些数据
  3. 这是我创建的工厂样本:

    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
            };
        }
    
    ]);
    

    正如您所看到的,这里有嵌套的承诺。当我尝试在AngularJS视图中使用来自最深层嵌套的承诺的数据时,导致我出现问题的原因是什么。具体来说,我想在ng-repeat语句中使用该承诺中的数据。但无论我尝试什么,它都不会出现。我试图在承诺中分配数据而不是返回,如下所示:

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

    这也不起作用。我尝试过各种其他方法,但我似乎无法将这些数据提供给视图。在console.log(data)调用中显示它没有问题,因此我知道数据正确回传。有没有人有解决这样问题的经验?

2 个答案:

答案 0 :(得分:0)

我建议你尽量避免嵌套的承诺。你可以看一下this blog post,它会让你看到如何避免'承诺汤'并承诺链接。

关于你的问题,我建议如下:

快速解决方案将是解决您的问题。您返回的工厂方法错误:

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
        };   
    }
]);

但是,您可以重构您的代码以实现您的承诺。类似的东西:

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
        };
    }
]);

现在,您可以这样做:

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

所有这一切都应该进行测试 - 如果你想要一个可行的解决方案,你可以添加一个plunker或jsbin ......

修改

我认为你这里有另一个问题。您在serviceFactoryservice之间混音。我不确定我是否理解这是否是同一服务,或者是谁。你能提供更详细的代码或添加plunker / jsbin等吗?

答案 1 :(得分:-2)

我已经编辑了这个答案,我最初删除了这个答案,因为我没有清楚地解释我的意思,并且它获得了一些downvotes(没有解释,但这是我的猜测)。无论如何,这是一个更完整的答案。

我怀疑你的问题是,无论你使用什么PaaS都没有意识到Angular,Angular同样也没有意识到PaaS。你在问题中说PaaS有返回promises的方法,但是如果Angular不知道这些promise,那么,当promises解决时,Angular不知道更新DOM。 Angular通过摘要循环执行此操作,这是Angular检查所有正在观察的内容以查看其是否已更改的位置。使用$q(或其他Angular服务,如$http)时,Angular知道在解析时会自动启动摘要周期。但是,当通过其他方式创建的承诺解决时,它不会启动摘要周期。

我认为这是您的代码中发生的事情。您的PaaS正在为您提供正确解决的承诺(您说您可以通过控制台查看结果),但您的HTML未更新。

I modified the plunkr我们正在努力证明这一点。我创建了一个模拟PaaS(不知道你在使用什么),使用jQuery创建promises并解析它们。如您所见,当promises解析时,结果将记录到控制台,但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>"
    };
  });

我原本建议您在捕获承诺的结果后添加$scope.$apply()来解决此问题。我已经分叉了Plunker,你可以看到它here,实际上更新了DOM。

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

然而,有一个更惯用的解决方案。当你从Angular中获得一个你需要在Angular中使用的承诺时,you can wrap that promise using $q.when(一个Angular意识的承诺),当外部承诺解决时,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对此问题here给出了很好的解释。