繁体   English   中英

JavaScript嵌套的承诺范围和AngularJS

[英]JavaScript nested promise scoping and AngularJS

我在JavaScript承诺中遇到了一个真正的问题,这是我在最近几个小时内一直在尝试解决的问题,但似乎无法解决。 我对诺言的经验有限,因此我对自己的做法完全错误的想法持开放态度。

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

  1. 连接到外部PaaS服务,该服务将返回承诺
  2. 在该承诺范围内,检索一些数据

这是我创建的工厂的示例:

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语句中使用该诺言中的数据。 但是,无论我尝试什么,它都不会显示。 我试图在promise中分配数据而不是返回,就像这样:

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

那也不行。 我尝试了多种其他方法,但似乎无法将这些数据显示在视图中。 让它显示在console.log(data)调用中没有问题,所以我知道数据可以正确返回。 有没有人有解决这样的问题的经验?

我建议您尽量避免嵌套承诺。 您可以看一下这篇博客文章 ,该文章将使您了解如何避免“无极汤”,而可以实现承诺链。

关于您的问题,我建议以下内容:

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

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

所有这些都应该经过测试-如果您想要一个可行的解决方案,则可以添加一个插件或jsbin ...

编辑

我认为您在这里还有另一个问题。 您正在serviceFactoryservice之间混合service 我不确定我是否了解这是同一服务还是谁。 您能否提供更详细的代码或添加plunker / jsbin等。

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

我怀疑您的问题是您使用的任何PaaS都不了解Angular,而Angular同样也不了解PaaS。 您在问题中说,PaaS具有返回承诺的方法,但是如果Angular不知道这些承诺,那么当承诺解决时,Angular不知道更新DOM。 Angular通过摘要循环执行此操作,在该摘要循环中,Angular会检查其正在监视的所有内容以查看其是否已更改。 当使用$q (或像其他角服务$http ),角知道自动揭开序幕消化周期时,他们解决。 但是,通过其他方式创建的承诺无法解决时,它不会启动摘要循环。

我认为这就是您的代码中正在发生的事情。 您的PaaS给您的诺言已经很好地解决了(您说可以通过控制台查看结果),但是您的HTML尚未更新。

我修改了我们正在研究的plunkr ,以演示其实际效果。 我创建了一个模拟PaaS(不知道您在使用什么),它使用jQuery创建了promise并解决了它们。 如您所见,当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>"
    };
  });

我最初建议您可以在捕获诺言的结果后添加$scope.$apply()来解决此问题。 我分叉了Plunker,您可以在这里看到它的确,实际上是在更新DOM。

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

但是,还有一个惯用的解决方案。 当您从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) 在这里对此问题做了很好的解释。

暂无
暂无

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

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