【问题标题】:Return already resolved promise返回已经解决的承诺
【发布时间】:2013-05-15 14:35:21
【问题描述】:

我有一个现有的项目,其中包含许多返回承诺的异步函数。我正在添加一些缓存,以便在某些情况下异步函数将同步完成,并希望尽可能使这段代码更短/更好:

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

例如,我有一个处理大多数 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,而只是做一些同步工作然后返回。虽然它需要返回一个承诺,因为无论调用什么它都会期待它,但它需要是一个“已经完成/解决的承诺”。目前我正在使用上面的第一组代码来做到这一点。看来一定有更好的办法。

【问题讨论】:

    标签: jquery jquery-deferred


    【解决方案1】:

    @Eselk,

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

    首先,:

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

    将简化为:

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

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

    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() 返回一个与 promise 兼容的 jqXHR 对象,该对象(在成功/错误时)立即被 .then() 修改和修改。
    • .then(null, ...) - 导致一个新的 Promise 被传递,使用与 $.ajax() 返回的原始 Promise 相同的值解析。因此,在“完成”(即 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恰好是一个没有特定含义的。

    【讨论】:

    • 感谢您提供的所有信息。我决定我需要在更高级别取消我的 ajax 调用,所以我现在在大多数地方都返回延迟,所以根据你的回答,我只需要 $.Deferred.resolve(); -- 干净整洁。
    • 我想你的意思是$.Deferred().resolve().promise();
    【解决方案2】:

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

    如果你根本不传递任何参数,jQuery.when() 将返回一个 已解决的承诺。

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


    注意事项:

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

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

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

    然后在完成时进行最终调用:

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

    例如

    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 的每次调用都将先前的承诺包装在新的承诺中。开销很小,您不再需要维护和评估一系列 Promise。
    • 没有值可以直接传递给最终函数。由于您通常不希望 parallel promises 的返回值,因此这是次要的。
    • 单个 Promise 的失败将提前终止最终等待,因此您需要确保所有 Promise 都将得到履行。

    【讨论】:

    • 没有参数将无法解析,但即使undefined 作为参数也会解析。奇怪。
    猜你喜欢
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-18
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多