8

我想用来$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"); }
);

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

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

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


更新

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

也就是说,这个问题正在寻找一种使用$q'swhen()函数的方法。当然欢迎客观上优越的技术,但不要回答这个具体问题。

4

2 回答 2

8

核心问题

您基本上是在尝试将现有的回调 API 转换为 promises。在 Angular$q.when中用于 Promise 聚合和 thenable 同化(即使用另一个 Promise 库)。不要害怕,因为您想要的完全可以实现,而无需每次都推迟手动操作。

延迟对象和 promise 构造函数

可悲的是,使用 Angular 1.x,你被过时的延迟接口卡住了,这不仅像你说的那样丑陋,而且也不安全(它有风险并且会同步抛出)。

您想要的称为 promise 构造函数,它是所有实现(Bluebird、Q、When、RSVP、本机 promise 等)都切换到的,因为它更好、更安全。

以下是您的方法使用本机承诺的外观:

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!
);

您当然可以复制此功能$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
    }
}

这会让你做:

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

promise.then(function(){

});

一个自动承诺助手

当然,为您的特定情况编写自动承诺方法同样容易。如果您使用许多具有回调约定的 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.
    };
}

这会让你做:

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

哪个更整洁

于 2014-05-13T08:35:56.170 回答
0

好的,这是我对我认为你想要的东西的解释。

我假设您想将非承诺回调与延迟/承诺集成?

下面的示例使用该wrapCallback函数包装两个非承诺回调,successCallback并且errCallback. 非承诺回调每个都返回一个值,该值将用于解决或拒绝延迟。

我使用一个随机数来确定是否应该解决或拒绝延迟,并使用非承诺回调的返回值来解决或拒绝它。

非角码:

function printArgs() {
    console.log.apply(console, arguments);
}

var printSuccess = printArgs.bind(null, "success");
var printFail = printArgs.bind(null, "fail");

function successCallback() {
    console.log("success", this);
    return "success-result";
}

function errCallback() {
    console.log("err", this);
    return "err-result";
}

function wrapCallback(dfd, type, callback, ctx) {
    return function () {
        var result = callback.apply(ctx || this, arguments);
        dfd[type](result);
    };
}

角代码:

var myApp = angular.module('myApp', []);

function MyCtrl($scope, $q) {
    var dfd = $q.defer();
    var wrappedSuccess = wrapCallback(dfd, "resolve", successCallback);
    var wrappedErr = wrapCallback(dfd, "reject", errCallback);
    var rnd = Math.random();
    var success = (rnd > 0.5);
    success ? wrappedSuccess() : wrappedErr();
    console.log(rnd, "calling " + (success ? "success" : "err") + " callback");
    dfd.promise.then(printSuccess, printFail);
}

随机数小于 的示例输出,0.5因此延迟被拒绝。

err Window /fiddlegrimbo/m2sgu/18/show/
0.11447505658499701 calling err callback
fail err-result
于 2013-09-03T23:29:31.653 回答