20

如何在异步 forEach 循环之后添加回调函数?

这是一些更好的上下文:

$scope.getAlbums = function(user, callback) {
    $scope.albumsList.forEach(function (obj, i) {
        $scope.getAlbum(user, obj.id, function(value){
            $scope.albums.push(value);
        });
    });
    // callback(); ???
};

$scope.getAlbums('guy123', function(){
    // forEach loop is all done!
    console.log($scope.albums)
});

控制器:

.controller('Filters', ['$scope','Imgur', function($scope, Imgur) {

    $scope.getAlbum = function(user, id, callback) {
        Imgur.album.get({user:user, id:id},    
            function(value) {
                return callback(value.data);
            }
        );
    }

    $scope.getAlbums = function(user, callback) {
        // Callback function at end of this forEach loop?
        // Request is async...
        $scope.albumsList.forEach(function (obj, i) {
            $scope.getAlbum(user, obj.id, function(value){
                $scope.albums.push(value);
            });
        });
    };
)]};

服务:

.factory('Imgur', function($resource) {
    return {
        album : $resource('https://api.imgur.com/3/account/:user/album/:id')
    }
});
4

4 回答 4

45

正如安德鲁所说$q,延迟对象的使用应该可以让你完成你的目标。

您想使用$q.all() 这将确保您的所有承诺对象都已解决,然后您可以回拨电话.then()

function MyCtrl($scope, $q, $http) {

    $scope.albumsList = [{
            id: 1,
            name: "11"
        }, {
            id: 2,
            name: "22"
        }
    ];
    $scope.albums = [];
    $scope.getAlbum = function(user, id, callback) {
        return $http.get("https://api.imgur.com/3/account/" + user + "/album/" + id).success(
            function(value) {
                return callback(value.data);
            }
        );
    }
    $scope.getAlbums = function (user, callback) {
        var prom = [];
        $scope.albumsList.forEach(function (obj, i) {
            prom.push($scope.getAlbum(user, obj.id, function(value){
                $scope.albums.push(value);
            }));
        });
        $q.all(prom).then(function () {
            callback();
        });
    };
    $scope.getAlbums('guy123', function () {
        alert($scope.albums.length);
    });
}

jsfiddle 上的示例

有效,但不适用于$http通话

使用 deferred 对象,您可以访问一个可以then()一起更改连续调用的 Promise。当您解析延迟对象时,它将执行 foreach,然后执行您提供的回调函数。我试图进一步简化您的示例,以便它可以在 jsfiddle 中使用。

function MyCtrl($scope, $http, $q) {

    $scope.albumsList = [{
        id: 1,
        name: "11"
    }, {
        id: 2,
        name: "22"
    }];
    $scope.albums = [];
    $scope.getAlbums = function (user, callback) {
        var deferred = $q.defer();
        var promise = deferred.promise;
        promise.then(function () {
            $scope.albumsList.forEach(function (obj, i) {
                $scope.albums.push(obj);
            });
        }).then(function () {
            callback();
        });
        deferred.resolve();
    };
    $scope.getAlbums('guy123', function () {
        alert($scope.albums.length);
    });
}

jsfiddle 上的示例

更多关于 deferred 和 promises 的阅读。

于 2013-05-08T00:11:59.060 回答
5
$scope.getAlbums = function(user, callback) {

        var promiseArr = [];
        $scope.albumsList.forEach(function (obj, i) {
            var anHttpPromise = 
            $scope.getAlbum(user, obj.id, function(value){
                $scope.albums.push(value);
            });
            promiseArr.push(anHttpPromise);
        });

        $q.all(promiseArr).then(function(){
            // This callback function will be called when all the promises are resolved.    (when all the albums are retrived)      
        })
    };

    $scope.getAlbum = function(user, id, callback) {
        var anHttpPromise = Imgur.album.get({user:user, id:id},    
            function(value) {
                return callback(value.data);
            }
        );
        return anHttpPromise;
    }

在上面的代码中:

  1. getAlbum是为了回报一个承诺。
  2. getAlbums为列表的每次迭代收集一个承诺
  3. 收集完所有的 Promise 后,将 Promise 数组传递给 $q.all
  4. $q.all方法返回一个最终的 Promise,一旦数组中的所有 Promise 都被解析,它的回调函数就会被触发。
于 2013-05-08T01:17:15.197 回答
4

使用定于 1.1.3 的$resourcepromise PR 提交,我能够$resource用 $q 包装调用并控制它们的异步行为的流程。

$scope.getAlbum = function(user, id, callback) {
    var promise = Imgur.album.get({user:user, id:id}).$promise.then(
        function( value ){
            return callback(value.data);
        },
        function( error ){
            //Something went wrong!
        }
    )
    return promise;
}

$scope.getAlbums = function(user, callback) {
    var prom = [];
    $scope.albumsList.forEach(function (obj, i) {
        var promise =
        $scope.getAlbum(user, obj.id, function(value){
            $scope.albums.push(value);
        });
        prom.push(promise);

    });

    $q.all(prom).then(function () {
        callback();
    });
};

$scope.getAlbums(user, function(){
    // Totally works, bro.
    console.log($scope.albums);
});
于 2013-05-08T18:40:47.273 回答
0

看起来延迟http://docs.angularjs.org/api/ng.$q并且特别链接承诺在这里可能很有用。

于 2013-05-07T23:37:27.090 回答