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();

    }
]);

但它仍然显示错误

控制台输出

159线点到线

orders.forEach(function(order,index) {  
4

3 回答 3

3

您的代码的问题在于您立即尝试使用语句获取数据console.log($scope.results);。我建议您阅读真正的异步编程,因为它将帮助您更好地理解为什么会发生这种情况。$http已经HttpPromise根据 AngularJS 文档返回了一个(这只是一个修改后的承诺),所以没有必要改变它。

问题是,进行该$http调用需要时间,并且您正在使用 for 循环对其进行迭代(这是您应该考虑的不好的做法$q.all())。如果您只有同步编程的经验,您可能会认为console.log($scope.results);for 循环完成后就会发生这种情况,但事实并非如此。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();
}]);
于 2014-10-07T16:34:36.533 回答
1

你可以嵌套 promise 让事情更有条理。由于您想要执行第一个 api 调用,并且一旦在结果中每行再完成一个,您还需要使用 $q 服务,就像 Christopher 提到的那样。

我没有测试过这段代码,但希望它能让你对我在说什么有所了解。

$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);
        });
};
于 2014-10-07T16:56:28.577 回答
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 秒后记录结果,此时所有单个订单都已获取。

在任何单个订单的请求完成console.log()之前,您的版本会立即调用。$http

稍微好一点的方法是$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;

    });

可能有更好的方法来解决这个问题,但你必须问一个更具体的问题。

于 2014-10-08T08:38:53.117 回答