Como sempre executar algum código quando uma promessa é cumprida em Angular.js

83

Em meu aplicativo Angular.js, estou executando algumas operações assíncronas. Antes de começar, abordo o aplicativo com um div modal e, depois que a operação for concluída, preciso remover o div, quer a operação tenha sido bem-sucedida ou não.

Atualmente eu tenho este:

LoadingOverlay.start(); 
Auth.initialize().then(function() {
    LoadingOverlay.stop();
}, function() {
    LoadingOverlay.stop(); // Code needs to be duplicated here
})

Funciona bem, mas prefiro algo mais limpo como este pseudo-código:

LoadingOverlay.start(); 
Auth.initialize().finally(function() { // *pseudo-code* - some function that is always executed on both failure and success.
    LoadingOverlay.stop();
})

Presumo que seja um problema bastante comum, por isso pensei que poderia ser feito, mas não consigo encontrar nada no documento. Alguma ideia se isso pode ser feito?

Laurent
fonte
Se você pode uma cadeia then(), então você certamente pode encadear outra ... .initialize().then(...).then(...). Não existe "finalmente" como tal; o manipulador final é o último especificado.
Beetroot-Beetroot
2
@ Beetroot-Beetroot, isso não funcionará porque se initialize()falhar, você ainda precisa declarar uma função de "sucesso" e uma função de "falha" e duplicar o código lá.
laurent de
Não vai funcionar, ou apenas deselegante?
Beetroot-Beetroot
1
Laurent, o que você deseja não está disponível no serviço $ q leve do Angular, que oferece promessas com apenas um método .then()- consulte "A API de promessa" aqui . A única liberdade é ter um .then()ou encadear vários .then()s. Você não é o primeiro a desejar uma API de promessa mais abrangente - o recurso que deseja é formalmente solicitado aqui .
Beetroot-Beetroot
1
Aparentemente, always(callback)não é implementado ou revertido no angular 1.2.6. Temos que usar finallyagora. Eu me pergunto por que a palavra reservada finallyé melhor do que always.
Aleyna

Respostas:

164

O recurso foi implementado nesta solicitação pull e agora faz parte do AngularJS. Ele foi inicialmente chamado de "sempre" e depois renomeado para finally, portanto, o código deve ser o seguinte:

LoadingOverlay.start(); 
Auth.initialize().then(function() {
    // Success handler
}, function() {
    // Error handler
}).finally(function() {
    // Always execute this on both error and success
});

Observe que, por finallyser uma palavra-chave reservada, pode ser necessário transformá-la em uma string para que não quebre em certos navegadores (como o IE e o Android):

$http.get('/foo')['finally'](doSomething);
Laurent
fonte
10
Para qualquer um que encontrar isso em uma pesquisa na web, o link na resposta se refere à função, alwaysmas foi alterado para finallycomo você pode ver neste commit (ou na fonte): github.com/angular/angular.js/commit/…
Austin Thompson
@AustinThompson, obrigado pela informação, atualizei o post.
Laurent
@ this.lau_ o finally () tem que ser a última chamada na corrente, ou posso acorrentar mais do que () está fora dele?
Brian
2
Você senhor, fez o meu dia!
JacobF de
4
@Brian finallyretorna uma promessa como o resto, então você pode encadear. No entanto (pelo menos em algumas versões do Angular) a conveniência sobrecarrega successe errorsó são adicionadas ao retorno imediato de $http, portanto, se você começar com, finallyperderá esses métodos.
drzaus
7

Estou usando o back-end do Umbraco versão 7.3.5 com AngularJS versão 1.1.5 e encontrei este tópico. Quando implementei a resposta aprovada, obtive o erro:

xxx (...). então (...). finalmente não é uma função

O que funcionou, entretanto, foi always. Se alguém mais usando uma versão antiga do AngularJS encontrar este tópico e não puder usar, finallyuse este código

LoadingOverlay.start(); 
Auth.initialize().then(function() {
    // Success handler
}, function() {
    // Error handler
}).always(function() {
    // Always execute this on both error and success
});
Ogglas
fonte
2

Para aqueles que não usam angularJS, e se você não tem problemas em detectar o erro (não tenho certeza se .finally () faria isso), você poderia usar .catch (). Then () para evitar o código duplicado.

Promise.resolve()
  .catch(() => {})
  .then(() => console.log('finally'));

O catch () pode acabar sendo útil de qualquer maneira para registro ou outra limpeza. https://jsfiddle.net/pointzerotwo/k4rb41a7/

PointZeroTwo
fonte