【问题标题】:New $.Deferred object with the old callbacks带有旧回调的新 $.Deferred 对象
【发布时间】:2014-12-30 14:22:13
【问题描述】:

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

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

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 成功
  • /asd 返回 404 Not Found前 3 次200 Success 一次作为模式(即三个 404 -> 一个 200 -> 三个 404 -> 一个 200 -> 三个 404 -> ...)。
  • /qqq 一直返回404 Not Found

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

这里的问题是:

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

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

【问题讨论】:

  • 从问题中实际上并不清楚您要达到什么目标。我明白你想克隆 deferred 的回调。但是为了什么?您想在每次点击时重新运行所有请求吗?为什么这些请求应该按顺序而不是并行运行?
  • @YuryTarabanko 请求必须是连续的,因为在实际产品中,第一个查询将表单字段保存到数据库表中,并使用 ID 进行响应。正在使用该 ID 发送其他查询。我确实想重新运行请求如果它们之前失败。当用户发送请求并且服务器说它失败时,我会警告用户失败,以便他们可以相应地更改输入(图像),然后重试。

标签: javascript jquery promise jquery-deferred


【解决方案1】:

Promises 表示单个值,受时间限制。您不能在概念上“重用”延迟或重置它 - 一旦它转换它就会粘住。有一些构造可以将 promise 推广到多个值(如 observables),但在这种情况下更复杂 - 每个请求只使用一个 deferred 可能会更好。

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

我们来看看$.get

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

如果发出帖子请求,您可以使用$.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 等自己解开结果。也可以在单个链中执行此操作(但是如果您无法访问以前的值,则你没有明确地保存它们。

郑重声明——这根本不是一个愚蠢的问题 :)

【讨论】:

  • 我把之前写的所有代码都去掉了。我用了你的例子。我为 3 个请求中的每一个都提供了一个 .fail 回调。我现在遇到的问题是当第二个请求失败时,第二个和第三个请求的失败回调都会触发。我不确定这是否会给我带来问题,但我想知道这是否是预期的行为。
  • @HakanAktaş 是的 - 在同步代码中,这就像让 function f() 调用 function g() 并让 g 抛出 - 如果您没有在 f 本身或内部捕获该异常g - 您的代码将到达调用 f 的任何人的 catch 处理程序。如果仍然不清楚,非常欢迎您提出单独的问题。
猜你喜欢
  • 2016-01-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
相关资源
最近更新 更多