【问题标题】:callback after async forEach AngularJS异步 forEach AngularJS 后的回调
【发布时间】:2013-05-02 01:02:31
【问题描述】:

如何在异步 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')
    }
});

【问题讨论】:

    标签: javascript angularjs


    【解决方案1】:

    正如 Andrew 所说,使用 $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);
        });
    }
    

    Example with this on jsfiddle

    有效,但不适用于 $http 呼叫

    使用延迟对象,您可以访问一个承诺,您可以在其中一起更改连续的then() 调用。当您解析延迟对象时,它将执行 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);
        });
    }
    

    Example on jsfiddle

    A bit more reading on deferred and promises.

    【讨论】:

    • 这正是我所需要的,但是,当我尝试使用外部资源数据时 --- 它不符合它的承诺 ;) ...您能否提供一个包含任何 API 数据的示例,例如推特?
    • 由于您使用的 $resource 也返回了一个 Promise,因此您需要修改您的代码,以便将 Promise 链接在一起并调用 resolve() 启动整个机制。跨度>
    • 查看 jsfiddle 上模拟 ajax 帖子的示例更新。
    • 我已经实施了您的解决方案并将最新更改推送到项目 @github.com/gigablox/angular-imgur-gallery/blob/master/app/js/… 但是我添加的代码仍然不会推迟执行那些 $resource 调用恐怕:/ ...我错过了什么?
    • 我误读了 api 文档,$resource 实际上根本没有返回一个 Promise 对象,你需要使用 $http 这样你就可以使用 $q.all() 我读到 $resource 正在运行获得承诺支持,但要等到以后的版本。
    【解决方案2】:
    $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,其回调 一旦数组中的所有承诺都被触发,函数将被触发 已解决。

    【讨论】:

    【解决方案3】:

    使用 1.1.3 中的 $resource 承诺 PR commit,我能够用 $q 包装 $resource 调用并控制其异步行为的流程。

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 你能分享一个与这个问题有关的例子吗:)?
      • 请看上面马克的回答。
      猜你喜欢
      • 2013-09-29
      • 2016-11-27
      • 2015-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-13
      相关资源
      最近更新 更多