AngularJS - aguarde a conclusão de várias consultas de recursos

105

Eu tenho uma única fábrica definida com ngResource:

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

Estou fazendo várias chamadas para o método de consulta definido nesta fábrica. As chamadas podem acontecer de forma assíncrona, mas preciso aguardar a conclusão das duas chamadas 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
    };
});

Existe uma maneira de fazer isso com as fábricas AngularJS definidas com ngResource, semelhante à funcionalidade $ .when (). Then () do jQuery? Eu preferiria não adicionar jQuery ao meu projeto atual.

Nathan Donze
fonte

Respostas:

200

Você vai querer usar promessas e $ q.all () .

Basicamente, você pode usá-lo para encerrar todas as suas chamadas $ resource ou $ http porque elas retornam promessas.

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
fonte
17
Os recursos não retornam promessas, eles retornam objetos a serem preenchidos no futuro. No entanto, na versão 1.1.3 instável , os recursos também têm $thenpropriedade, mas não expõem nenhum objeto de promessa. A exposição $promisecompleta seria em 1.1.4
Umur Kontacı
@ UmurKontacı Isso infelizmente não está no angular 1.1.4!
nh2
Detalhes sobre os recursos não são problemas de promessas podem ser encontrados neste tópico e nesta solicitação de pull .
nh2
1
Esta resposta mostra como escrevê-lo depois de implementado.
nh2
3
Sua resposta é muito útil e acredito que seja a forma mais sensata de converter recursos em promessas no angular atual. Pode ser útil adicionar que na documentação de $q, à qual você vinculou, isso garante que a matriz de resultado está na mesma ordem que a matriz de promessa.
nh2
20

Acho que a melhor solução é:

$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
fonte
1
Para mim, funcionou sem $ promessa no final ... Assim como: Account.query ({type: 'billing'}), Account.query ({type: 'shipping'})
georgeos
12

A solução de Ben Lesh é a melhor, mas não é completa. Se você precisar lidar com condições de erro - e, sim, você precisa - então você deve usar o catchmétodo na API de promessa como este:

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

});

Se você não definir catche todas as suas promessas falharem, o thenmétodo nunca será executado e, portanto, provavelmente deixará sua interface em um estado ruim.

Nick A. Watts
fonte