对$q和承诺的困惑

Confusion about $q and promises

本文关键字:承诺      更新时间:2023-09-26

我已经读了几个小时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
            },
var deferred = $q.defer(); //Why am I doing this?

这是你所做的延迟反模式,因为你不理解承诺。实际上,在应用程序逻辑中使用$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;
     }
 }
promise是一个不会立即执行的函数。相反,您"承诺"在延迟对象完成处理时对其状态作出响应。从本质上讲,您正在创建一个模拟多个线程的执行链。一旦一个延迟对象完成,它就会返回一个状态代码,调用函数能够响应状态代码,并在必要时返回自己的状态代码,等等
var deferred = $q.defer();

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

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

这是实际的函数,但您不是在这里调用它。你正在制作一个变量来引用它,这样你就可以监视它

var rValue = promise.then(function(){

.then((是在promise成功时将执行的函数。在这里,您正在内联一个函数,该函数将在promise的工作成功后执行。

}, function(error){

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

deferred.resolve(promise);

这就是您实际告诉延迟对象执行代码,等待结果,然后根据情况执行成功或失败代码的地方。promise被添加到执行链中,但您的代码现在可以自由地继续,而无需等待和挂断。一旦promise完成并处理成功或失败,您的一个回调函数就会进行处理。

 console.log(rValue); 

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

return rValue;

你是在向来电者回报你的承诺。你的函数是一个promise,它正在调用一个可能正在调用另一个promet的promise,等等。如果你的函数不是promise,那么即使你正在调用的代码没有被阻塞,当你等待执行完成时,你的代码也会变成一个块。通过将承诺链接在一起,你可以定义你应该如何回应,而不必在那一刻做出回应。您不是直接返回结果,而是"承诺"返回成功或失败,在这种情况下,通过您正在调用的函数传递成功或失败。

当您要执行一项将花费未知时间的任务时,或者您不在乎它何时完成时,您会使用延迟承诺-异步任务。只要您调用的代码告诉您任务已完成,您所关心的就是返回结果

在您的函数中,您应该做的是返回您正在创建的递延的承诺。调用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
            }

您所做的是混合承诺。正如你在代码注释中所说,

angularfire中的$changepassword返回一个承诺

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

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
    });
}]);