关于$ q和承诺的困惑

时间:2014-04-30 12:20:02

标签: javascript angularjs promise q angularfire

我已经阅读了Kris Kowal的Q和angularjs $ q变量几个小时了。但对于我的生活,我无法弄清楚这是如何运作的。

目前,我的服务中包含此代码:

resetpassword: function (email, oldPassword, newPassword) {

                var deferred = $q.defer(); //Why am I doing this?
                var promise = auth.$changePassword(email, oldPassword, newPassword); //$changepassword in angularfire returns a promise
                console.log(deferred); //Object with resolve, reject, notify and promise attrs
                var rValue = promise.then(function(){
                        //successcallback
                        return 1; //if changepassword succeeds it goes here as expected
                    }, function(error){
                        //errorcallback
                        return error.code; //if changepassword fails (wrong oldpass for example) it goes here, also works
                    }
                );
                deferred.resolve(promise); //Should I do this? I thought my promise was resolved in the callbacks?
                console.log(rValue); //This outputs another promise? Why not 1 or error.code, how the am I ever going to get these values??
                return rValue; //I want 1 or error.code to go back to my controller
            },

4 个答案:

答案 0 :(得分:2)

var deferred = $q.defer(); //Why am I doing this?

这是你正在做的deferred anti-pattern,因为你不了解承诺。在应用程序逻辑中使用$q.defer()几乎没有充分的理由。

您需要从函数中返回一个承诺:

resetpassword: function(email, oldPassword, newPassword) {
    return auth.$changePassword(email, oldPassword, newPassword)
           .then(function() { return 1; })
           .catch(function(e) { return e.code });
}

用法是:

resetpassword(...)
.then(function(num) {
    if (num === 1) {

    }
    else {

    }
});

有助于思考如何用同步代码编写函数:

 resetpassword: function(email, oldPassword, newPassword) {
     try {
         auth.$changePassword(email, oldPassword, newPassword)
         return 1;
     }
     catch (e) {
         return e.code;
     }
 }

答案 1 :(得分:1)

promise是一个不立即执行的函数。相反,你有希望'在完成它的处理时响应延迟对象的状态。实质上,您正在创建一个模拟多个线程的执行链。一旦延迟对象完成,它就会返回一个状态代码,调用函数能够响应状态代码,并在必要时返回它自己的状态代码等。

var deferred = $q.defer();

在这里,您要创建一个延迟对象,以保存稍后将执行的回调。

var promise = auth.$changePassword(email, oldPassword, newPassword);

这是实际的功能,但你不是在这里调用它。您正在创建一个变量来引用它,以便您可以监视它。

var rValue = promise.then(function(){

.then()是承诺成功时将执行的函数。在这里,您将概述一项功能,该功能将在承诺工作成功后执行。

}, function(error){

然后链接错误函数,如果promise失败则执行该错误函数。除了返回错误代码之外,你实际上并没有做任何事情,但你可以自己做一些事情来回应失败。

deferred.resolve(promise);

这是您实际告诉延迟对象执行代码,等待结果,然后根据需要执行成功或失败代码的点。承诺被添加到执行链中,但您的代码现在可以自由继续,无需等待和挂起。一旦承诺完成并处理成功或失败,您的一个回调函数将处理。

 console.log(rValue); 

您的函数未返回此处的实际值,因为您不希望函数在继续之前停止并等待响应,因为这会阻止程序直到函数完成。

return rValue;

您正在向呼叫者回复您的承诺。你的函数是一个承诺,它调用一个可能正在调用另一个promise等的promise,如果你的函数不是一个promise,那么即使你调用的代码没有阻塞,你的代码也会在你等待的时候成为一个块。执行完成。通过将承诺链接在一起,您可以定义如何在没有响应的情况下做出响应。您没有直接返回结果,而是承诺'返回成功或失败,在这种情况下,通过您正在调用的函数成功或失败。

答案 2 :(得分:1)

你所做的是混合的承诺。当你输入代码注释时,

   angularfire中的$ changepassword返回一个承诺

基本上,var deferred = $q.defer()使用deferred.promise创建新承诺,但您已经使用$changePassword创建了承诺。如果我理解你正在尝试做什么,你只需要这样做。

resetpassword: function (email, oldPassword, newPassword) {               
    auth.$changePassword(email, oldPassword, newPassword).then(function(result)       {
        console.log(result); // success here
    }, function(err){
        console.log(err); // error here
    });
}

编辑:

根据Robins的评论,如果您想在控制器中处理结果,而不是在服务中调用then()函数,请将promise本身返回给控制器。这是一个例子:

服务功能:

resetpassword: function (email, oldPassword, newPassword) {
    // return the promise to the caller
    return auth.$changePassword(email, oldPassword, newPassword);
}

控制器功能:

.controller('MyCtrl', ['$scope', 'myService', function($scope, myService){
    // get the promise from the service
    myService.resetPassword($scope.email, $scope.oldPassword, $scope.newPassword).then(function(result){
        // use 1 here
    }, function(err){
        // use err here
    });
}]);

答案 3 :(得分:1)

当您要执行将花费不明时间的任务时,或者在完成任务时您并不关心 - 异步任务时,您使用延期承诺。所有你关心的是,只要你所调用的代码告诉你任务完成,就会得到一个结果。

您在函数中应该做的是返回您正在创建的延迟的承诺。您调用resetpassword的代码应等待此承诺得到解决。

function (email, oldPassword, newPassword) {

                var deferred = $q.defer(); //You are doing this to tell your calling code when the $changepassword is done. You could also return the promise directly from angularfire if you like but of course the return values would be different.
                var promise = auth.$changePassword(email, oldPassword, newPassword); //$changepassword in angularfire returns a promise
                console.log(deferred); //Object with resolve, reject, notify and promise attrs
                promise.then(function(){
                        //successcallback
                        deferred.resolve(1); //if changepassword succeeds it goes here as expected
                    }, function(error){
                        //errorcallback
                        deferred.reject(0); //if changepassword fails (wrong oldpass for example) it goes here, also works
                    }
                );
                // Quite right, you should not do this.
                console.log(rValue); //This outputs another promise? Why not 1 or error.code, how the am I ever going to get these values??
                return deferred.promise; // Return the promise to your controller
            }