新 $.带有旧回调的延迟对象

New $.Deferred object with the old callbacks

本文关键字:延迟 对象 回调      更新时间:2023-09-26

如果这是一个愚蠢的问题,请原谅我。我已经尝试了几个小时,我的大脑刚刚停止工作。

我有这样一个系统,它由三个 AJAX 调用组成。第一次调用的服务器响应通常是 200 成功;但是第二个和第三个查询很脆弱,因为它们是图像上传,而在服务器端,我有太多的验证规则,以至于客户端的图像大多失败。

window.AjaxCall = function () {
    // to pass to $.ajax call later
    this.args = arguments;
    // xhr status
    this.status = null;
    // xhr results (jqXHR object and response)
    this.xhrResponse = {};
    this.dfr = new $.Deferred();
    // to provide an easier interface
    this.done = this.dfr.done;
    this.fail = this.dfr.fail;
    this.then = this.dfr.then;
};
AjaxCall.prototype.resetDfr = function () {
    this.dfr = new $.Deferred();
};
AjaxCall.prototype.resolve = function () {
    this.dfr.resolve(
            this.xhrResponse.result,
            this.xhrResponse.jqXHR
    );
    this.resetDfr();
};
AjaxCall.prototype.reject = function () {
    this.dfr.reject(
            this.xhrResponse.jqXHR
    );
    this.resetDfr();
};
AjaxCall.prototype.query = function () {
    var _this = this;
    // if query hasn't run yet, or didn't return success, run it again
    if (_this.status != 'OK') {
        $.ajax.apply(_this, _this.args)
                .done(function (result, textStatus, jqXHR) {
                    _this.xhrResponse.result = result;
                    _this.xhrResponse.jqXHR = jqXHR;
                    _this.resolve();
                })
                .fail(function (jqXHR) {
                    _this.xhrResponse.jqXHR = jqXHR;
                    _this.reject();
                })
                .always(function (a, b, c) {
                    var statusCode = (typeof c !== 'string'
                            ? c
                            : a).status;
                    if (statusCode == 200) {
                        _this.status = 'OK';
                    }
                });
    }
    // if query has been run successfully before, just skip to next
    else {
        _this.resolve();
    }
    return _this.dfr.promise();
};

AjaxCall类如上所示,我像这样连续三次调用:

var First = new AjaxCall('/'),
        Second = new AjaxCall('/asd'),
        Third = new AjaxCall('/qqq');
First.then(function () {
    console.log('#1 done');
}, function() {
    console.error('#1 fail');
});
Second.then(function () {
    console.log('#2 done');
}, function() {
    console.error('#2 fail');
});
Third.then(function () {
    console.log('#3 done');
}, function() {
    console.error('#3 fail');
});
var toRun = function () {
    First.query()
            .then(function () {
                return Second.query();
            })
            .then(function () {
                return Third.query()
            });
};
$('button').click(function () {
    toRun();
});

这些代码位于测试环境中。通过测试环境,我的意思是一个简单的HTML页面和基本的服务器支持调试。

  • 主页 (/) 始终返回 200 成功
  • /asd3 次返回 404 未找到,200 成功一次作为模式返回(即三个 404 ->一个 200 ->三个 404 ->一个 200 ->三个 404 -> ... )。
  • /qqq 始终返回 404 未找到

当我单击页面上的唯一按钮时,第一个查询返回成功,第二个查询按预期失败。当我第二次单击该按钮时,第一个查询跳过,因为它上次成功,第二个查询再次失败,也如预期的那样。

这里的问题是:

  • 在我使用 resetDfr 方法之前,因为dfr已解析或拒绝,它不再对resolvereject方法做出反应。
  • 当我以示例中所示的方式调用 resetDfr 方法时dfr能够再次解决或拒绝,但旧dfr的回调未与新dfr对象绑定,我找不到将旧回调克隆到新dfr的方法。

你有什么建议来完成我在这里要做的事情?

承诺表示受时间约束的单个值。你不能在概念上"重用"一个延迟的或重置它 - 一旦它过渡它就会坚持下去。有一些构造可以将承诺推广到多个值(如可观察量),但在这种情况下这些结构更复杂 - 最好只使用每个请求的一个延迟。

jQuery的AJAX已经提供了一个promise接口。您的代码大多是多余的 - 您可以并且应该考虑使用现有的工具。

让我们看看$.get

  • 它已经返回了一个承诺,因此您无需创建自己的延期。
  • 它已经使用了浏览器缓存,除非您的服务器禁止 HTTP 缓存或浏览器拒绝它,否则只会在到达正确的响应后向服务器发出一个请求(假设您没有显式将{cache: false}传递给其参数。

如果发出 post 请求,您可以使用 $.post 或更一般的$.ajax用于任意选项。

您的代码大致如下所示:

$("button").click(function(){
    var first = $.get("/");
    var second = first.then(function(){
       return $.get("/asd");
    });
    var third = second.then(function(){
       return $.get("/qqq");
    });
});

我将它们放在变量中的原因是,以便您以后可以通过执行first.then等操作自己解包结果。在单个链中也很有可能执行此操作(但是如果您不显式保存以前的值,您将无法访问以前的值。

为了记录 - 这根本不是一个愚蠢的问题:)