返回已经解决的承诺

时间:2013-05-20 22:55:24

标签: jquery jquery-deferred

我有一个现有的项目,它有很多异步函数可以返回promises。我正在添加一些缓存,以便在某些情况下异步函数将同步完成,并希望在可能的情况下使代码更短/更好:

        return $.Deferred(function(def) { def.resolve(); }).promise();

例如,我有一个Data Service类来处理大多数看起来像这样的AJAX请求:

function DataService() {

    var self = this;

    self.makeRequest = function(functionName, data) {
        return $.Deferred(function(def) {

            var jsonData = JSON.stringify(data);

            $.ajax({
                type: "POST",
                url: "WebService.asmx/" + functionName,
                data: jsonData,
                contentType: "application/json; charset=utf-8",
                dataType: "json",
                error: function(xhr, status, error) {
                    var ex;
                    try {
                        ex = eval("(" + xhr.responseText + ")");
                        ex.message = ex.Message;
                        ex.Message = undefined;
                    } catch (ex2) {
                        ex = { message: "Invalid Response From Server:\r\n" + xhr.responseText };
                    }
                    if (ex.message == "LoginRequired") {
                        app.viewModels.main.loginRequired(true);
                    }
                    else {
                        app.showError(ex.message);
                    }
                    def.reject(ex.message);
                }
            });
        }).promise();
    }
}

然后我在另一个当前总是调用makeRequest的类中有一个函数:

self.deleteMe = function()
{
   return app.dataservice.makeRequest('deleteItem');
}

我想更新deleteMe函数,以便它不总是调用makeRequest,而只是做一些同步工作然后返回。它需要返回一个承诺,因为无论什么叫做它都会期待,但它需要是一个“已经完成/已解决的承诺”。目前我正在使用上面的第一组代码来做到这一点。似乎必须有更好的方法。

2 个答案:

答案 0 :(得分:45)

@Eselk,

根据我的经验,很少需要$.Deferred(function(def) {...});构造,但我想在某些情况下它可能非常有用。

首先,:

return $.Deferred(function(def) { def.resolve(); }).promise();

将简化为:

return $.Deferred().resolve().promise();

其次,在DataService.makeRequest()中,您可以通过利用$.Deferred来避免显式.then()的需要,如下所示:

function DataService() {
    var self = this;
    self.makeRequest = function (functionName, data) {
        return $.ajax({
            type: "POST",
            url: "WebService.asmx/" + functionName,
            data: JSON.stringify(data),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        }).then(null, function (xhr, status, error) {
            var ex;
            try {
                ex = eval("(" + xhr.responseText + ")");
                ex.message = ex.Message;
                ex.Message = undefined;
            } catch (ex2) {
                ex = {
                    message: "Invalid Response From Server:\r\n" + xhr.responseText
                };
            }
            if (ex.message == "LoginRequired") {
                app.viewModels.main.loginRequired(true);
            } else {
                app.showError(ex.message);
            }

            return ex.message;
        });
    };
}

这里的关键方面是:

  • $.ajax()返回与承诺兼容的jqXHR对象,该对象(成功/错误)立即由.then()执行并修改。
  • .then(null, ...) - 导致传递新的承诺,使用与$.ajax()返回的原始承诺相同的值解析。因此,在“完成”(即ajax成功)条件下,.then()完全透明。
  • return ex.message; - 导致新承诺传递,被ex.message拒绝。

nett效果应该与原始代码相同,但恕我直言,链接.then()比在$.Deferred()回调中设置所有内容要清晰得多。

顺便说一句,您可以通过设置适当的HTTP标头服务器端来避免对eval("(" + xhr.responseText + ")")等的需要,以便您的'.Message'直接显示为status参数(或xhr.status?)失败回调。例如,在PHP中,您可以执行以下操作:

$message = "my custom message";
header("HTTP/1.1 421 $message");
exit(0);

我确信ASP提供相同的功能。

IIRC,任何4xx系列状态代码都可以完成这项工作。 421恰好是一个没有特定含义的人。

答案 1 :(得分:25)

只需使用return $.when();返回已经解决的承诺

  

如果你没有传递任何参数,jQuery.when()将返回一个   解决了承诺。

参考: https://api.jquery.com/jquery.when/

注意:

  • 这与return $.when(undefined);相同,这导致了以下相当酷的技巧,避免使用数组和apply

如果您想等待可变数量的承诺完成并行,那么您可以在循环中使用此模式:

var promise;   // Undefined is treated as an initial resolved promise by $.when
while (insomeloop){
    promise = $.when(promise, newpromise);
}

然后在完成后进行最后一次通话:

promise.then(function(){
    // All done!
});

e.g。

var promise;$.when
var $elements = $('.some-elements');
$elements.each(function(){
     var $element = $(this).fadeout();
     promise = $.when(promise, $element.promise());
});
promise.then(function(){
    // All promises completed!
});

缺点是次要的:

  • when的每次调用都会在新承诺中包含先前的承诺。一个小的开销,你不再需要维护和评估一系列的承诺。
  • 没有值可以直接传递给最终函数。由于您通常不希望来自 parallel promises 的返回值,因此这是次要的。
  • 单一承诺的失败将提前中止最后的等待,因此您需要确保所有承诺都能实现。