Finally gets called immediately before promise gets fulfilled
Asked Answered
V

3

10

I'm trying to execute a check once promise gets fulfilled in angularjs.

request.then(function(res){
    $ionicLoading.hide();
    deferred.resolve(res);
  }, function(res){
    $ionicLoading.hide();
    deferred.reject(res);
  })['finally'](function(res){
      alert(res)
    }
  )

But the alert is coming as 'undefined'.

  1. Is that expected or I'm doing anything wrong? I thought it'll get called only when promise gets resolved/rejected.
  2. What is the right way to achieve this?

Thanks

Viviyan answered 4/9, 2014 at 16:52 Comment(6)
why are you using array notation? and not .finally() ?Damaging
@EliteOctagon from the doc: "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 and Android 2.x compatible."Nazarite
I believe the finally callback isn't meant to take parameters because I believe in general, finally clauses are guaranteed to be called after some execution to allow you to clean up resources and whatnot.Nazarite
@MannyD in the same manner, you can use the alias .fin() rather than .finally() github.com/kriskowal/q/wiki/… , finally() is meant to take parameters, a functionDamaging
nonetheless, I think I and bmleite both address your question of why you're getting undefined and what the right way to achieve it is. You're just missing a return statement for the promises.Damaging
Hi, could you possibly select an answer if one helped you?Damaging
D
10

Edit/Update ... This is not the most awesome way of doing, but a simple and straightforward way. You need to keep track of what you want to to finally alert as you go down the chain of promises (assuming you have multiple), and just store it in a variable.

var something = null;
request.then(function(response){
    $ionicLoading.hide();
    something = response;
  }, function(reason){
    $ionicLoading.hide();
    something = reason;
  }).finally(function(){ 
      alert(something);
  });

A plunker to demonstrate:

http://plnkr.co/edit/DrqeaCAYWTQ4iWY0NPeq?p=preview

Damaging answered 4/9, 2014 at 17:41 Comment(0)
H
6

You are doing it correctly, the problem is that the value that is being passed to the finally callback is the same value returned by success or error callbacks. Since you are not returning anything, the value is undefined.

If you add the return clauses to each callback it should work:

request.then(function(res){
  $ionicLoading.hide();
  deferred.resolve(res);
  return res;
}, function(res){
  $ionicLoading.hide();
  deferred.reject(res);
  return res;
})['finally'](function(res){
    alert(res)
  }
)

Edit

It seems Angular's finally implementation is not quite prepared to pass a value to the callback. However there is another way to produce the effect you want, just replace the finally by another then:

request.then(function(res){
  $ionicLoading.hide();
  deferred.resolve(res);
  return res;
}, function(res){
  $ionicLoading.hide();
  deferred.reject(res);
  return res;
}).then(function(res){
    alert(res)
  }
)

Since the promises are executed sequentially, the final then will run lastly. And since you are not returning any other promises on the success and error callbacks, the last then will only need a success callback.

Ultimately you could also use something like this:

...)['finally'](function(){ }).then(function(res){
    alert(res)
  }
)
Haemorrhage answered 4/9, 2014 at 17:40 Comment(1)
This is only partially correct. It's true that the success/fail callbacks need to return something to pass to the finally call, but this code will still output undefined.Nazarite
H
4

The finally callback is called with no arguments. It does, however, return a promise passing the results along.

Even fixing for that, you're not returning anything in your callbacks, so nothing is being passed along. To illustrate this:

angular.module('myApp', [])
.run( function ($q) {
    var defer = $q.defer();
    defer.promise.then(
        function ( res ) { console.log('s1> '+res); },
        function ( res ) { console.log('e1> '+res); }
    )
    .then(
        function ( res ) { console.log('s2> '+res); },
        function ( res ) { console.log('e2> '+res); }
    )

    defer.reject(1);
});

Gives this:

e1> 1
s2> undefined

Notice that the second then was "successful" because the reject didn't get passed on.

Make sure that your callbacks return something. And if you want to fall to the errback in subsequent thens make sure that you return a rejection.

var defer = $q.defer();
defer.promise.then(
        function ( res ) { console.log('s1> '+res); return res; },
        function ( res ) { console.log('e1> '+res); return $q.reject(res); }
)
.then(
        function ( res ) { console.log('s2> '+res); return res; },
        function ( res ) { console.log('e2> '+res); return $q.reject(res); }
)

Putting that together gives something like this:

var defer = $q.defer();
defer.promise.then(
        function ( res ) { console.log('s1> '+res); return res; },
        function ( res ) { console.log('e1> '+res); return $q.reject(res); }
)
.then(
        function ( res ) { console.log('s2> '+res); return res; },
        function ( res ) { console.log('e2> '+res); return res; }
)
.finally (
        function ( res ) {
            console.log('f0> '+res+','+arguments.length);
        }
)
.then(
        function ( res ) { console.log('s3> '+res); return res; },
        function ( res ) { console.log('e3> '+res); return $q.reject(res); }
)

defer.reject('foo');

Resulting in:

e1> foo
e2> foo
f0> undefined,0
s3> foo

Notice that the errback in the second then returned res instead of a rejection, so the callback of the finally's then was called.

Harem answered 4/9, 2014 at 17:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.