2013-05-07 12 views
20

Wie füge ich eine Callback-Funktion nach einem asynchronen forEach Loop hinzu? HierRückruf nach Async forEach AngularJS

ist etwas besser Kontext:

$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:

.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); 
      }); 
     }); 
    }; 
)]}; 

Service:

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

Antwort

44

Wie Andrew sagte Verwendung von $q und das verzögerte Objekt sollte es Ihnen ermöglichen, Ihr Ziel zu erreichen.

Sie wollen $q.all() verwenden Dies wird sicherstellen, dass alle Ihre Versprechen Objekte aufgelöst werden und dann können Sie Ihren Anruf Rückruf in .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

Arbeiten aber nicht mit $http Anrufe

Mit dem verzögerten Objekt erhalten Sie Zugang zu einem Versprechen, wo Sie aufeinanderfolgendeändern könnenruft zusammen an. Wenn Sie das verzögerte Objekt auflösen, führt es den foreach aus und führt dann Ihre von Ihnen angegebene Rückruffunktion aus. Ich habe versucht, dein Beispiel ein wenig weiter zu vereinfachen, damit es in jfiddle funktioniert.

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.

+0

Dies ist jedoch genau das, was ich brauchte, wenn ich mit externen Ressourcendaten versuche --- es hält es nicht für versprechend;) ... Könnten Sie bitte ein Beispiel mit irgendwelchen API-Daten wie Twitter geben? –

+0

Da Sie die Ressource '$ resource' verwenden, die auch eine Verheißung zurückgibt, müssen Sie Ihren Code so ändern, dass er die Versprechen zusammenfasst, und der Aufruf von' resolve() 'startet den gesamten Mechanismus. –

+0

Siehe Update mit Beispiel auf jsfiddle, die simulierten Ajax Post. –

0

I t sieht aus wie verzögert http://docs.angularjs.org/api/ng.$q und Verkettungen Versprechen könnte hier sinnvoll sein.

+0

Können Sie ein Beispiel im Zusammenhang mit diesem Problem teilen :)? –

+0

Bitte lesen Sie Mark Antwort oben. –

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

In dem obigen Code:

  1. Die getAlbum wird, eine Versprechen zurückzukehren.
  2. ein Versprechen für jede Iteration der getAlbums Liste Sammeln
  3. Sobald alle Versprechungen gesammelt werden, wird das Versprechen Array $q.all
  4. bestanden Die $q.all Methode stattdessen gibt eine endgültige Zusage, deren Rückruf Funktion einmal ausgelöst wird Alle Versprechen im Array sind gelöst.
+0

Ich habe Ihre Lösung implimiert und die neuesten Änderungen an das Projekt @ https://github.com/gigablox/angular-imgur-gallery/blob/master/app/js/controllers.js weitergeleitet, aber es verspricht immer noch nicht. Mabye, ich habe etwas verpasst? –

4

Mit dem $resource Versprechen PR commit für 1.1.3 geplant, war ich in der Lage $resource Anrufe mit $ q zu wickeln und den Fluss ihrer async behavoir zu steuern.

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