jQuery 返回已解决的承诺

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16659630/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 17:33:12  来源:igfitidea点击:

Return already resolved promise

jqueryjquery-deferred

提问by eselk

I have an existing project that has a lot of asynchronous functions that return promises. I'm adding some caching so that in some cases the asynchronous functions will complete synchronously and would like to make this code shorter/better if possible:

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

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

For example, I have a Data Service class that handles most AJAX requests that looks like this:

例如,我有一个处理大多数 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();
    }
}

Then I have a function in another class that currently always calls makeRequest:

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

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

I want to update the deleteMe function so that it doesn't always call makeRequest, and instead just does some synchronous work and then returns. It needs to return a promise though, because whatever called it will be expecting that, but it needs to be an "already completed/resolved promise". Currently I am using the first set of code above to do that. Seems like there must be a better way.

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

回答by Beetroot-Beetroot

@Eselk,

@埃塞尔克,

In my experience, the $.Deferred(function(def) {...});construction is rarely needed, though I guess it can be quite useful in some circumstances.

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

Firstly, :

首先, :

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

will simplify to :

将简化为:

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

Secondly, in DataService.makeRequest()you can avoid the need for an explicit $.Deferredby exploiting .then(), as follows :

其次,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;
        });
    };
}

The key aspects here are :

这里的关键方面是:

  • $.ajax()returns a promise-compatible jqXHR object, which (on success/error) is immediately acted upon and modified by .then().
  • .then(null, ...)- causes a new promise to be passed on, resolved with the same values as the original promise returned by $.ajax(). Thus, under the 'done' (ie ajax success) condition, .then()is completely transparent.
  • return ex.message;- causes a new promise to be passed on, rejected with ex.message.
  • $.ajax()返回一个与 Promise 兼容的 jqXHR 对象,该对象(成功/错误时)立即被.then().
  • .then(null, ...)- 导致传递新的承诺,并使用与 返回的原始承诺相同的值进行解析$.ajax()。因此,在'done'(即ajax成功)条件下,.then()是完全透明的。
  • return ex.message;- 导致一个新的承诺被传递,被拒绝ex.message

The nett effect should be identical to your original code though, IMHO, chaining .then()is significantly cleaner than setting everything up inside a $.Deferred()callback.

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

By the way, you may be able to avoid the need for eval("(" + xhr.responseText + ")")etc by setting an appropriate HTTP header server-side such that your '.Message' appears directly as the statusargument (or xhr.status?) of the fail callback. For example, in PHP you would do something like :

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

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

I'm sure ASP offers the same capability.

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

IIRC, any 4xx series status code will do the job. 421 just happens to be one without a specific meaning.

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

回答by Gone Coding

Simply use return $.when();to return an already resolved promise.

简单地用于return $.when();返回一个已经解决的 promise

If you don't pass it any arguments at all, jQuery.when() will return a resolved promise.

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

Reference:https://api.jquery.com/jquery.when/

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



Notes:

笔记:

  • This is the same as return $.when(undefined);which leads to the following rather cool trick which avoids any use of arrays and apply.
  • 这与return $.when(undefined);导致以下相当酷的技巧相同,该技巧避免使用数组和apply

If you want to wait for a variable number of promises to complete in parallelthen you can use this pattern in a loop:

如果要等待可变数量的 promise并行完成则可以在循环中使用此模式:

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

then make a final call on completion with:

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

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

The downsides are minor:

缺点是轻微的:

  • each call to whenwraps the previous promise in a new promise. A minor overhead and you no longer need to maintain and evaluate an array of promises.
  • no values can be directly passed through to the final function. As you typically do not want the return values from parallel promises, this is minor.
  • failure of a single promise will abort the final wait early, so you need to ensure all promises will be fulfilled.
  • 每次调用都when将之前的承诺包装在一个新的承诺中。少量开销,您不再需要维护和评估一系列承诺。
  • 没有值可以直接传递给最终函数。由于您通常不想要并行 promises的返回值,因此这是次要的。
  • 单个承诺的失败将提前中止最后的等待,因此您需要确保所有承诺都将得到履行。