AngularJS: espere a que se completen varias consultas de recursos

105

Tengo una sola fábrica definida con ngResource:

App.factory('Account', function($resource) {
    return $resource('url', {}, {
        query: { method: 'GET' }
    });
});

Estoy haciendo varias llamadas al método de consulta definido en esta fábrica. Las llamadas pueden ocurrir de forma asincrónica, pero necesito esperar a que se completen ambas llamadas antes de continuar:

App.controller('AccountsCtrl', function ($scope, Account) {
    $scope.loadAccounts = function () {
        var billingAccounts = Account.query({ type: 'billing' });
        var shippingAccounts = Account.query({ type: 'shipping' });

        // wait for both calls to complete before returning
    };
});

¿Hay alguna manera de hacer esto con las fábricas de AngularJS definidas con ngResource, similar a la funcionalidad $ .when (). Then () de jQuery? Preferiría no agregar jQuery a mi proyecto actual.

Nathan Donze
fuente

Respuestas:

200

Querrá usar promesas y $ q.all () .

Básicamente, puede usarlo para envolver todas sus llamadas $ resource o $ http porque devuelven promesas.

function doQuery(type) {
   var d = $q.defer();
   var result = Account.query({ type: type }, function() {
        d.resolve(result);
   });
   return d.promise;
}

$q.all([
   doQuery('billing'),
   doQuery('shipping')
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...
});
Ben Lesh
fuente
17
Los recursos no devuelven promesas, devuelven objetos para completar en el futuro. Sin embargo, en la versión 1.1.3 inestable , los recursos también tienen $thenpropiedad pero no exponen ningún objeto de promesa. Exponer por $promisecompleto estaría en 1.1.4
Umur Kontacı
@ UmurKontacı ¡Desafortunadamente esto no está en angular 1.1.4!
NH2
Los detalles sobre el problema de los recursos no son promesas se pueden encontrar en este hilo y en esta solicitud de extracción .
nh2
1
Esta respuesta muestra cómo escribirlo una vez que se implementa.
nh2
3
Su respuesta es muy útil y creo que es la forma más sensata de convertir recursos en promesas en el angular actual. Podría ser útil agregar que en la documentación de $q, a la que se vinculó, garantiza que la matriz de resultados está en el mismo orden que la matriz de promesa.
nh2
20

Creo que una mejor solución es:

$q.all([
   Account.query({ type: 'billing' }).$promise,
   Account.query({ type: 'shipping' }).$promise
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...
});
Tales Mundim Andrade Porto
fuente
1
Para mí funcionó sin $ promise al final ... Al igual que: Account.query ({type: 'billing'}), Account.query ({type: 'shipping'})
georgeos
12

La solución de Ben Lesh es la mejor pero no está completa. Si necesita manejar condiciones de error, y sí, lo hace, entonces debe usar el catchmétodo en la API de promesa de esta manera:

$q.all([
   doQuery('billing'),
   doQuery('shipping')
]).then(function(data) {
   var billingAccounts = data[0];
   var shippingAccounts = data[1];

   //TODO: something...

}).catch(function(data) {

   //TODO: handle the error conditions...

}).finally(function () {

  //TODO: do final clean up work, etc...

});

Si no define catchy todas sus promesas fallan, el thenmétodo nunca se ejecutará y, por lo tanto, probablemente dejará su interfaz en mal estado.

Nick A. Watts
fuente