具有旧回调的新$ .Deferred对象

时间:2014-12-30 14:22:14

标签: javascript jquery promise jquery-deferred

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

我有这样的系统,包含三个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成功模式(即三个404 - >一个200 - >三个404 - >一个200 - >三个404 - > ......)。
  • / qqq 一直返回 404 Not Found

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

这里的问题是:

    在我使用resetDfr方法之前
  • ,因为dfr已被解决或拒绝,它对resolve和{{1}没有反应方法了。
  • 当我按照示例中显示的方式调用reject方法时,resetDfr能够再次得到解决或拒绝,但是旧的回调{ {1}}未与新dfr对象绑定,我无法找到将旧回调克隆到新dfr的方法。

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

1 个答案:

答案 0 :(得分:1)

Promises代表按时间限制的单个值。你无法在概念上重复使用"延迟或重置它 - 一旦它转换它坚持。有些构造将promises概括为多个值(如observables),但在这种情况下这些构造更复杂 - 只使用一个延迟的每个请求可能更好。

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

让我们看一下$.get

  • 它已经返回一个承诺,因此您不需要创建自己的延期。
  • 它已经使用了浏览器缓存,除非你的服务器禁止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等来自行解包结果。很可能在单个链中执行此操作(但是您如果您没有明确保存,则无法访问以前的值。

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