How to always run some code when a promise is fulfilled in Angular.js

71,694

Solution 1

The feature has been implemented in this pull request and is now part of AngularJS. It was initially called "always" and then later renamed to finally, so the code should be as follow:

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

Note that since finally is a reserved keyword, it might be necessary to make it a string so that it doesn't break on certain browsers (such as IE and Android Browser):

$http.get('/foo')['finally'](doSomething);

Solution 2

I'm using Umbraco version 7.3.5 back end with AngularJS version 1.1.5 and found this thread. When I implemented the approved answer I got the error:

xxx(...).then(...).finally is not a function

What did work however was always. If anyone else using an old version of AngularJS finds this thread and can't use finally use this code instead

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

Solution 3

For those not using angularJS, and if you're ok with catching the error (not sure if .finally() would do that), you could use .catch().then() to avoid the duplicated code.

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

The catch() might end up being useful anyway for logging or other cleanup. https://jsfiddle.net/pointzerotwo/k4rb41a7/

Solution 4

I would use ngView to render the content of the page and trigger the removal of you modal on the event $viewContentLoaded. See http://docs.angularjs.org/api/ng.directive:ngView for that event and http://docs.angularjs.org/api/ng.$rootScope.Scope for the $on event listener.

Share:
71,694

Related videos on Youtube

laurent
Author by

laurent

Updated on July 05, 2022

Comments

  • laurent
    laurent almost 2 years

    In my Angular.js application, I'm running some asynchronous operation. Before it starts I cover the application with a modal div, then once the operation is complete, I need to remove the div, whether the operation was successful or not.

    Currently I have this:

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

    It works well, however I would prefer to have something cleaner like this pseudo-code:

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

    I assume it's quite a common problem, so I was thinking it could be done but cannot find anything in the doc. Any idea if it can be done?

    • Beetroot-Beetroot
      Beetroot-Beetroot about 11 years
      If you can chain one then(), then you can surely chain another ... .initialize().then(...).then(...). There's no "finally" as such; the final handler is the last one specified.
    • laurent
      laurent about 11 years
      @Beetroot-Beetroot, that won't work because if initialize() fails, you still need to declare both a "success" function and a "fail" function and duplicate code in there.
    • Beetroot-Beetroot
      Beetroot-Beetroot about 11 years
      Won't work, or just inelegant?
    • laurent
      laurent about 11 years
      @Beetroot-Beetroot, I mean it really won't work. I have updated my example to clarify what I mean. As you can see in example 1, the code needs to be duplicated, which is not just inelegant but also harder to maintain. In example 2 (pseudo-code), the function will be executed no matter what, which avoids duplicate code. Here just chaining then() functions won't help since I'd still need to handle both failure and success (even though I don't care if it succeeded or not). Perhaps what I'm trying to do cannot currently be done though.
    • Beetroot-Beetroot
      Beetroot-Beetroot about 11 years
      Laurent, what you want isn't currently available in Angular's lightweight $q service, which provides promises with just one method, .then() - see "The Promise API" here. The only freedom is to have one .then() or to chain multiple .then()s. You are not the first to wish for a more extensive promise API - the feature you want is formally requested here.
    • OZ_
      OZ_ almost 11 years
    • laurent
      laurent almost 11 years
      @OZ_, I should have mentioned it but I actually ended up implementing the feature in this pull request - github.com/angular/angular.js/pull/2424 It's been accepted and I guess should be part of the main Angular.js release soon.
    • OZ_
      OZ_ almost 11 years
      Thank you, @Laurent. It's implemented in 1.1.5 which is on google CDN today :) I think it will not be worse if $http will have same functional "out of the box" :) At this moment I'm trying to figure out and learn how can I wrap $http with $q to get flow, described in your question.
    • laurent
      laurent almost 11 years
      @OZ_, I didn't check but I would assume $http makes use of the built-in promise service, doesn't it?
    • OZ_
      OZ_ almost 11 years
      @Laurent, I thought built-in promise and $q - different things. But if not.. Will check right now.
    • OZ_
      OZ_ almost 11 years
      IT WORKS!!! Thank you VERY much!
    • Aleyna
      Aleyna over 10 years
      Apparently always(callback) is not implemented or rolled back in angular 1.2.6. We have to use finally now. I wonder why the reserved word finally is better than always.
    • laurent
      laurent over 10 years
      @Aleyna, interesting, when I've implemented the feature, the consensus was to use always since that's what jQuery is using and because finally is a reserved JS keyword. Eventually, it seems they went for finally anyway with the caveat Because 'finally' is a reserved word in JavaScript and reserved keywords are not supported as property names by ES3, you'll need to invoke the method like 'promise['finally'](callback)' to make your code IE8 compatible. Why not, but it seems a bit more trouble than needed.
    • laurent
      laurent over 10 years
      And actually Android Browser will also throw an error if using finally without quotes.
    • Aleyna
      Aleyna over 10 years
      @Laurent, I have checked out the discussion on your pull request. I would second always as well. Perhaps maintainers wished to stick with Q's original api.
  • Austin Thompson
    Austin Thompson almost 10 years
    For anyone finding this from a web search, the link in the answer referred to the function always but it was changed to finally as you can see in this commit (or in the source): github.com/angular/angular.js/commit/…
  • laurent
    laurent almost 10 years
    @AustinThompson, thanks for the information, I have updated the post.
  • jan
    jan over 9 years
    @this.lau_ does the finally() have to be the last call in the chain, or can I chain more then()s off of it?
  • JacobF
    JacobF over 9 years
    You sir, made my day!
  • drzaus
    drzaus over 8 years
    @Brian finally returns a promise just like the rest, so you can chain it. However (at least on some versions of Angular) the convenience overloads success and error are only added to the immediate return of $http, so if you start with finally you'll lose those methods.
  • greenoldman
    greenoldman almost 8 years
    @drzaus, for the record, success and error were deprecated. docs.angularjs.org/api/ng/service/$http