9

假设我有一个$.Deferred和一个jqXHR对象。有没有办法将绑定到延迟的所有处理程序(然后,总是,完成,失败)转移到 XHR 对象(据我了解,它是延迟的扩展)?


这是我的想法:

$.ajaxOne = function(options) {
    var xhr = null;
    return function() {
        if(xhr) xhr.abort();
        xhr = $.ajax(options).always(function() {
            xhr = null;
        });
    }
}

我想创建一个类似于 的函数,$.ajax只是如果您快速连续多次调用它,它将中止最后一个请求,只完成最近的一个请求。这在您想要验证用户输入的许多场景中很有用。

例如,您可能想检查是否使用了用户名,但如果他们在您开始 ajax 调用后再次开始在用户名字段中输入,您不必关心最后的结果,只关心最近的结果。

另外,我认为请求不能保证以它们发出的相同顺序返回(我想取决于您的服务器设置),因此您也可能遇到同步问题。

无论如何,上面代码的问题是因为它返回一个函数,你可以随时执行你的ajax调用,但是你不能将你的完成处理程序绑定到它。所以我必须以某种方式混合延迟处理程序并将它们重新绑定到 XHR 对象。

4

3 回答 3

2

假设我有一个 $.Deferred 和一个 jqXHR 对象。有没有办法将绑定到延迟的所有处理程序(然后,总是,完成,失败)转移到 XHR 对象(据我了解,它是延迟的扩展)?

或多或少,是的,但不是您预期的方式。而不是“移动处理程序”,您只需使用 XHR 延迟解决延迟(具有处理程序)。这将使 deferred 采用 ajax 承诺的状态——或者不采用,因为 jQuery 不兼容Promise A+ 。因此,您需要手动将触发器作为处理程序:

var deferred = $.Deferred(),
    xhr = $.ajax(…);
xhr.done(deferred.resolve).fail(deferred.reject).progress(deferred.notify);

然而,不鼓励这样的使用,只要xhr在你需要的地方使用deferred——它们是平等的。或者xhr.then()用来创建一个全新的 promise 对象,它的解析与xhrwill 完全一样。

无论如何,上面代码的问题是因为它返回一个函数,你可以随时执行你的ajax调用,但是你不能将你的完成处理程序绑定到它。所以我必须以某种方式混合延迟处理程序并将它们重新绑定到 XHR 对象。

您仍然可以xhr从该返回函数返回每个对象,并将您的处理程序绑定到该函数。如果它被中止,它的error处理程序将被调用。

$.ajaxOne = function(options) {
    var xhr = null;
    return function(name) {
        options.data = name;
        if (xhr) xhr.abort();
        return xhr = $.ajax(options).always(function() {
//      ^^^^^^
            xhr = null;
        });
    }
}
var checkUserAccount = $.ajaxOne({…});
$input.keyup(function(e) {
    checkUser(this.value).done(function(ajaxResult) {
        // do anything here with the ajaxResult from the latest call
        // if there was another keyup event, this callback never fires
    });
});

另外,我认为请求不能保证以它们发出的相同顺序返回(我想取决于您的服务器设置),因此您也可能遇到同步问题。

如果您abort在再次调用该函数时调用每个旧的,则不会 - 这将保持一次最多只有一个活动的 ajax 请求的不变性。

我想创建一个类似于 $.ajax 的函数,只是如果你快速连续多次调用它,它将中止最后一个请求,只完成最近的一个请求。

听起来很像一个事件流。你会想看看函数响应式编程!

于 2014-08-17T21:10:51.807 回答
0

在这里拉小提琴。

我没有使用延迟对象就实现了。

keyup 事件 -

$("#uID").keyup(function () {
    console.log("KeyUp")
    var textElement = this;
    clearTimeout(textElement.timer);
    if(textElement.xhrReq && textElement.xhrReq.abort){
        textElement.xhrReq.abort();
    }
    textElement.timer = setTimeout(function(){
        console.log("Invoking validation : " + textElement.value);
        validateUID(textElement);
    }, 1000);
});

在 validateUID() 中,我已将整个 XHR 对象分配给输入元素的属性 -

textElement.xhrReq = $.ajax({
...
});

一个缺点——我承认它就在那里——我们需要将整个 XHR 对象保留在元素中。

于 2014-08-17T18:58:25.510 回答
0

想出了这个:

function AjaxOne(options) {
    this.options = options;
    this._xhr = null;
    this._always = [];
    this._success = [];
    this._fail = [];
};

$.extend(AjaxOne.prototype, {
    always: function(cb) {
        this._always.push(cb);
        return this;
    },
    done: function(cb) {
        this._success.push(cb);
        return this;
    },
    fail: function(cb) {
        this._fail.push(cb);
        return this;
    },
    then: function(success, fail) {
        this._success.push(success);
        this._fail.push(fail);
        return this;
    },
    run: function(options) {
        if(this._xhr) {
            this._xhr.abort();
        }
        this._xhr = $.ajax($.extend({},options,this.options,{context:this}))
            .always(function() {
                this._xhr = null;
                for(var i=0; i<this._always.length;++i) this._always[i].apply(this,arguments);
            })
            .done(function() {
                for(var i=0; i<this._success.length;++i) this._success[i].apply(this,arguments);
            })
            .fail(function() {
                for(var i=0; i<this._fail.length;++i) this._fail[i].apply(this,arguments);
            });
    }
});

到目前为止似乎工作得很好......但它没有回答我原来的问题。

所以一个全面的答案是:你不能将回调从一个延迟复制到另一个。我尝试以各种方式制作 deferred 的副本,但$.extend({}, myDeferred)我什么也做不了。我认为您必须手动复制每个方法并触发适当的回调,类似于我所做的。

您可以按照 Arun 在他的评论中建议的那样将回调传播到原始的 deferred (尽管我认为他的语法略有偏差;根据文档,您不需要.apply那里;'with' 方法的目的是允许你设置一个上下文)。就我而言,我希望能够多次触发这些方法,因此这不适用于我的场景。

于 2013-10-31T23:06:51.710 回答