2014-10-07 37 views
1

这是我工作的代码。

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', 'Order', '$http', 
     function($scope, $rootScope, Order, $http) { 
      $scope.results = []; 
      $scope.getData = function() { 
        $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id).success(function(data){ 
         for (var i = 0; i < data.length; i++) { 
          $http.get('api/orders/seller/?business_id=' + data[i].business_id).success(function(data1){ 
           // console.log(data1);   
           $scope.results[i] = data1; 
          }); 
         } 
         console.log($scope.results); 
        }); 
      }; 
      $scope.getData(); 
     }]); 

问题是,$ scope.results是空的,而功能正常工作。有人说这是由于$ http的异步性质。你能修改代码来使用promise来避免错误吗?

现在我更新的代码所示

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', '$http','$q', 
    function($scope, $rootScope, $http, $q) { 

     $scope.results = []; 

     function _getOrdersById(id) { 
      return $http.get('api/orders/business/?user_id=' + id); 
     } 

     function _parseOrders(orders) { 
      var _promises = []; 

      orders.forEach(function (order, index) { 
       var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
        $scope.results[index] = response; 
       }); 

       _promises.push(_promise); 
      }); 

      return $q.all(_promises); 
     } 

     $scope.getData = function() { 
      _getOrdersById($rootScope.user.user_id) 
       .then(_parseOrders) 
       .then(function() { 
        console.log($scope.results); 
       }, function (error) { 
        console.error(error); 
       }); 
     }; 


     $scope.getData(); 

    } 
]); 

,但它仍然显示错误

console output

159线点到线

orders.forEach(function(order,index) { 
+0

$ http已经是一个承诺。 – 2014-10-07 16:36:57

回答

1

在您的使用案例中,console.log($scope.results)来得太早。

如果你做了“正确的方式”或这个“不要在家里做,因为它是愚蠢的”方式,你会得到结果。

错误办法做到这一点,只是在这里告诉你:

var MyCtrl = app.controller(function($scope, $timeout, $http, $rootScope) { 
     $scope.results = []; 
     $scope.getData = function() { 
       $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id).success(function(data){ 
        for (var i = 0; i < data.length; i++) { 
         $http.get('api/orders/seller/?business_id=' + data[i].business_id).success(function(data1){ 
          // console.log(data1);   
          $scope.results[i] = data1; 
         }); 
        } 

        // THIS is the part I'm changing: 
        $timeout(function() { 

         console.log($scope.results); 
        }, 10000); 
       }); 
     }; 
     $scope.getData(); 
    }); 

只是假设在这里,但我认为这应该工作。为什么?因为我们在10秒钟后记录结果,所有单个订单都被提取。

您的版本立即在$http个别订单的任何请求已完成之前称为console.log()

稍微好一点的方法是按其他人的建议做$q.all()。而更好的办法是将这一切都转化为服务。

稍好的方式做你的事:

// Controller. Notice how simplified it seems now, comparing to the original 
// version. We're just passing results to and from the $scope, which is what 
// controllers should do. 

var MyController = app.controller(function($scope, OrderService, $rootScope) { 

    OrderService.getOrders($rootScope.user.user_id) 
    .then(function(results) { 

     // only putting $scope.results live here, you can have 
     // an ng-show="$scope.results.length" or something to show a spinner 
     // or similar while loading. 
     $scope.results = results; 
    }); 
}); 

// Then, your service would do all the work 

var OrderService = app.service(function($http) { 

    // we're going to play it with a service, just copy/pasting Lowe's answer here 
    // and modifying a few bits to take out controller part 

    var _results = []; 

    // This returns a promise. Angular can accept a promise and will wait until resolved. 
    function _getOrdersById(id) { 
     return $http.get('api/orders/business/?user_id=' + id); 
    } 

    // This also returns a promise. 
    function _parseOrders(orders) { 
     var _promises = []; 

     orders.forEach(function (order, index) { 
      var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
       _results[index] = response; 
      }); 

      _promises.push(_promise); 
     }); 

     return $q.all(_promises); 
    } 

    // Anything attached to _this_ is "public" for a service 
    this.getOrders = function(userId) { 

     // Create a defered object to return to callers 
     var d = $q.defer(); 

     // Get that first promise 
     _getOrdersById(userId) 

     // the next promise in order (_parseOrders), will receive whatever 
     // _getOrdersById return 
     .then(_parseOrders) 

     // The next promise won't be called until $q.all of _parseOrders is called. 
     .then(function() { 

      // finally resolve our original promise. This returns to 
      // the caller of the service 
      d.resolve(_results); 

     }, function (error) { 

      console.error(error); 
      d.reject(error); 
     }); 

     // this return happens almost before any of the $http calls above. 
     // But since you're returning a promise, angular knows this it have to wait 
     // until that d.resolve() or d.reject() somewhere in the async response handlers. 
     return d.promise; 

    }); 

可能有更好的方式来打破这种下来,但你要问更具体一点的问题。

3

与您的代码的问题是你立即试图获取数据的事实声明console.log($scope.results);。我建议你阅读异步编程的真实内容,因为它可以帮助你更好地理解为什么会发生这种情况。 $http根据AngularJS文档(这只是一个修改的承诺)已经返回HttpPromise,所以没有意义改变它。

问题是,使$http调用需要时间,并且您正在用for循环遍历它(这是不好的做法,您应该考虑$q.all())。如果你只有同步编程经验,你可能会认为一旦for循环完成就会发生console.log($scope.results);,但事实并非如此。 for循环中的调用将会异步,并且在您回答问题之前您将进行日志记录。

TL; DR:你真的需要研究javascript和你甚至可以从服务器:)

appControllers.controller('MyaSellerOrderCtrl', ['$scope', '$rootScope', 'Order', '$http', '$q', 
function($scope, $rootScope, Order, $http, $q) { 
    $scope.results = []; 
    $scope.getData = function() { 
     $http.get('api/orders/business/?user_id=' + $rootScope.user.user_id) 
      .success(function(data){ 

      var promises = data.map(function (item) { 
       return $http.get('api/orders/seller/?business_id=' + item.business_id); 
      }); 

      $q.all(promises).then(function(data) { 

       //Use this to see what the data is 
       console.log(data); 
       //Add some logic here if data isn't exactly the array you want 
       $scope.results = data; 

      }, function (err) { 
       //Do some error handling 
      }); 

     }); 
    }; 
    $scope.getData(); 
}]); 
+0

我明白成功是异步的。但我需要成功功能与每次迭代同步工作。我会怎么做,这样我就可以得到每个迭代的控制台输出 – 2014-10-07 16:57:41

+0

我肯定会建议按照我所说的去查看'$ q.all'。如果你正确地实现了它,你将能够在每次迭代中做一些逻辑,并且得到最终的总结果(这取决于你的需要,尽管如此很难给出一个通用的例子)。 – 2014-10-07 17:32:02

+0

更新了帖子。请检查它 – 2014-10-08 08:38:21

1

可以通过嵌套承诺让事情得到一个响应返回之前记录的变量异步的概念有点结构。既然你想做第一个API调用,并且一次在结果中每行完成一次,你还需要使用$ q服务,就像克里斯托弗提到的一样。

我还没有测试过这段代码,但它希望能给你一些关于我在说什么的想法。

$scope.results = []; 

function _getOrdersById(id) { 
    return $http.get('api/orders/business/?user_id=' + id); 
} 

function _parseOrders(orders) { 
    var _promises = []; 

    // Stop parsing if orders isn't an array 
    if (!angular.isArray(orders)) { 
     return; 
    } 

    orders.forEach(function (order, index) { 
     var _promise = $http.get('api/orders/seller/?business_id=' + order.business_id).then(function (response) { 
      $scope.results[index] = response; 
     }); 

     _promises.push(_promise); 
    }); 

    return $q.all(_promises); 
} 

$scope.getData = function() { 
    _getOrdersById($rootScope.user.user_id) 
     .then(_parseOrders) 
     .then(function() { 
      console.log($scope.results); 
     }, function (error) { 
      console.error(error); 
     }); 
}; 
+0

谢谢。但是这也显示了错误。 – 2014-10-08 08:34:04

+0

我已通过修改和错误更新了帖子。 – 2014-10-08 08:37:52

+0

checkoutController.js在线159上的内容是什么?你正在使用什么版本的AngularJs? – 2014-10-08 09:06:52