Angular - 是.finally()在链式承诺决策树中的每个路径的末尾调用吗?

时间:2016-05-06 15:19:16

标签: angularjs angular-promise

我有以下链式承诺序列:

$scope.promisesInProgress = true
myService.myFirstPromise(id)
    .then(function(data){
        $scope.firstResponse = data;
        return myService.mySecondPromise(id);
    })
    .then(function(data){
        $scope.secondResponse = data;
    })
    .finally(function(){
        $scope.promisesInProgress = false;
    });

无论前两个承诺的成功/失败是什么,最终都会调用finally()回调函数?

例如,如果myFirstPromise()返回400响应,则永远不会调用mySecondPromise() - 但我认为仍然会抛出finally()块?如果mySecondPromise()返回400(并且永远不会设置$scope.secondResponse)并且两个promises都返回200,那么同样如此。

2 个答案:

答案 0 :(得分:2)

Angular 1.x $q服务受Kris Kowal的Q启发,基于docs

  

finally(callback,notifyCallback) - 允许您观察承诺的履行或拒绝,但是在不修改最终值的情况下这样做。无论承诺被拒绝还是已解决,这对于释放资源或进行必要的清理都非常有用。有关详细信息,请参阅完整规范。

是的,无论myFirstPromise已解决或拒绝,finally()块始终会被调用

更新

要注意,在finally()解决(或拒绝)之前会调用myFirstPromise mySecondPromise块,因为myFirstPromisemySecondPromise是不同的承诺实例,mySecondPromisemyFirstPromise已解决

之后创建的承诺实例

答案 1 :(得分:1)

我编写了一个Jasmine测试,看看是否在每个函数执行时调用finally()块,无论链接的promise返回什么。

describe('myController Test Suite', function(){

    var q, scope, deferred, myService;

    // Initialize the Pointivo module
    beforeEach(function(){
        module('myApp');
    });

    // Mock out fake service
    beforeEach(function(){
        myService = {
            myFirstPromise: function(){
                deferred = q.defer();
                // TEST ME
                deferred.resolve('first promise response');
                return deferred.promise;
            },
            mySecondPromise: function(){
                deferred = q.defer();
                // TEST ME
                deferred.resolve('second promise response');
                return deferred.promise;
            },
            myThirdPromise: function(){
                deferred = q.defer();
                // TEST ME
                deferred.resolve('third promise response');
                return deferred.promise;
            }
        };
        spyOn(myService, 'myFirstPromise').and.callThrough();
        spyOn(myService, 'mySecondPromise').and.callThrough();
        spyOn(myService, 'myThirdPromise').and.callThrough();
    });

    // Assign controller scope and service references
    beforeEach(inject(function($controller, $rootScope, $q){
        scope = $rootScope.$new();
        q = $q;
        $controller('myController', {
            $scope: scope,
            myService: myService
        });
    }));

    describe('finally test', function(){
        it('should always hit the finally statement', function(){
                scope.finallyStatementFlag = false;
            scope.test();
            scope.$apply();
            expect(scope.finallyStatementFlag).toBeTruthy();
        });
    });

});

以上基于控制器的假设:

myApp.controller('myController', function($scope, myService){

    $scope.finallyStatementFlag = false;

    $scope.test = function(){
        myService.myFirstPromise()
            .then(function(data){
                console.log(data);
                return myService.mySecondPromise()
            })
            .then(function(data){
                console.log(data);
                return myService.myThirdPromise();
            })
            .then(function(data){
                console.log(data);
            })
            .finally(function(){
                console.log('finally statement');
                $scope.finallyStatementFlag = true;
            });
    }

});

即使您在我们定义deferred.resolve()的{​​{1}}回调中更改了deferred.reject()beforeEach()中的任何一个或全部,上述内容也会通过。

Fiddle example