使用Angular的$ q.when()解析延迟的原因

wil*_*ler 8 javascript promise angularjs angular-promise

我想用来$q.when()包装一些非承诺的回调.但是,我无法弄清楚如何在回调中解决承诺.我在匿名函数内部做什么强迫$q.when()我解释?

promises = $q.when(
    notAPromise(
        // this resolves the promise, but does not pass the return value vvv
        function success(res) { return "Special reason"; },
        function failure(res) { return $q.reject('failure'); }
    ) 
);

promises.then(
    // I want success == "Special reason" from ^^^
    function(success){ console.log("Success: " + success); },
    function(failure){ console.log("I can reject easily enough"); }
);
Run Code Online (Sandbox Code Playgroud)

我想复制的功能是这样的:

promises = function(){
    var deferred = $q.defer();

    notAPromise(
        function success(res) { deferred.resolve("Special reason"); },
        function failure(res) { deferred.reject('failure'); }
    ); 

    return deferred.promise;
};

promises.then(
    // success == "Special reason"
    function(success){ console.log("Success: " + success); },
    function(failure){ console.log("I can reject easily enough"); }
);
Run Code Online (Sandbox Code Playgroud)

这很好,但when()看起来很好.我只是无法将解析消息传递给then().


UPDATE

有更好,更强大的方法来做到这一点.$q同步抛出异常,正如@Benjamin指出的那样,主要的承诺库正朝着使用完整的Promise代替Deferreds.

这就是说,这个问题是寻找一种方式来做到这一点使用$qwhen()功能.客观上优越的技术当然是受欢迎的,但不回答这个具体问题.

Ben*_*aum 8

核心问题

您基本上是在尝试将现有的回调API转换为承诺.在Angular $q.when中用于promise聚合,以及用于可能的同化(即,使用另一个promise库).不要害怕,因为你想要的东西是完全可行的,而不是每次都推迟手册.

延迟对象和promise构造函数

遗憾的是,对于Angular 1.x,你会遇到过时的延迟界面,这不仅像你说的那样丑陋,而且也是不安全的(它有风险并且同步抛出).

你想要的是所谓的promise构造函数,它是所有实现(Bluebird,Q,When,RSVP,本地承诺等)正在切换到的,因为它更好,更安全.

以下是本机承诺的方法:

var promise = new Promise(function(resolve,reject){
    notAPromise(
        function success(res) { resolve("Special reason") },
        function failure(res) { reject(new Error('failure')); } // Always reject
    )                                                          // with errors!
);
Run Code Online (Sandbox Code Playgroud)

$q当然可以复制此功能:

function resolver(handler){
    try {
        var d = $q.defer();
        handler(function(v){ d.resolve(v); }, function(r){ d.reject(r); });
        return d.promise;
    } catch (e) {
        return $q.reject(e); 
        // $exceptionHandler call might be useful here, since it's a throw
    }
}
Run Code Online (Sandbox Code Playgroud)

哪个会让你这样做:

var promise = resolver(function(resolve,reject){
    notAPromise(function success(res){ resolve("Special reason"),
                function failure(res){ reject(new Error("failure")); })
});

promise.then(function(){

});
Run Code Online (Sandbox Code Playgroud)

一个自动的promisification帮助器

当然,为您的特定情况编写自动promisification方法同样容易.如果您使用回调约定处理大量API,则fn(onSuccess, onError)可以执行以下操作:

function promisify(fn){
    return function promisified(){
         var args = Array(arguments.length + 2);
         for(var i = 0; i < arguments.length; i++){
             args.push(arguments[i]);
        }
        var d = $q.defer();
        args.push(function(r){ d.resolve(r); });
        args.push(function(r){ d.reject(r); });
        try{
            fn.call(this, args); // call with the arguments
        } catch (e){  // promise returning functions must NEVER sync throw
            return $q.reject(e);
            // $exceptionHandler call might be useful here, since it's a throw
        }
        return d.promise; // return a promise on the API.
    };
}
Run Code Online (Sandbox Code Playgroud)

这可以让你做到:

var aPromise = promisify(notAPromise);
var promise = aPromise.then(function(val){
    // access res here
    return "special reason";
}).catch(function(e){
    // access rejection value here
    return $q.reject(new Error("failure"));
});
Run Code Online (Sandbox Code Playgroud)

哪个更整洁