Jquery 如何将所有处理程序从一个转移到另一个?

Jquery 如何将所有处理程序从一个转移到另一个?,jquery,promise,jquery-deferred,Jquery,Promise,Jquery Deferred,假设我有一个$.Deferred和一个jqXHR对象。是否有一种方法可以将所有绑定到deferred(然后,总是,done,fail)的处理程序转移到XHR对象(据我所知,它是deferred的扩展) 以下是我的想法: $.ajaxOne = function(options) { var xhr = null; return function() { if(xhr) xhr.abort(); xhr = $.ajax(options).alwa

假设我有一个
$.Deferred
和一个
jqXHR
对象。是否有一种方法可以将所有绑定到deferred(然后,总是,done,fail)的处理程序转移到XHR对象(据我所知,它是deferred的扩展)


以下是我的想法:

$.ajaxOne = function(options) {
    var xhr = null;
    return function() {
        if(xhr) xhr.abort();
        xhr = $.ajax(options).always(function() {
            xhr = null;
        });
    }
}
我想创建一个函数,类似于
$.ajax
,只是如果您快速连续多次调用它,它将中止最后一个请求,只完成最近一个请求。这在许多需要验证用户输入的场景中非常有用

例如,您可能希望检查是否使用了用户名,但如果在您启动ajax调用后,他们再次开始在用户名字段中键入用户名,则您不关心最后的结果,只关心最近的结果

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

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

得出了以下结论:

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);
            });
    }
});
函数AjaxOne(选项){
this.options=选项;
这是.\u xhr=null;
这个;
这就是成功;
这个。_fail=[];
};
$.extend(AjaxOne.prototype{
始终:功能(cb){
这个。总是。推(cb);
归还这个;
},
完成:功能(cb){
这是成功推送(cb);
归还这个;
},
失败:功能(cb){
这是失败的推送(cb);
归还这个;
},
然后:功能(成功,失败){
这是成功;
这个.\u fail.push(失败);
归还这个;
},
运行:功能(选项){
如果(这个){
这是。xhr.abort();
}
this.xhr=$.ajax($.extend({},options,this.options,{context:this}))
.always(函数(){
这是.\u xhr=null;
对于(var i=0;i

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

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对象保留在元素中

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

或多或少,是的,但不是以您期望的方式。您不需要“移动处理程序”,而只需使用XHR延迟来解析延迟的(有处理程序的)。这将使延迟采用ajax承诺的状态,或者不采用,因为jQuery不兼容。因此您需要手动将触发器作为处理程序放置:

var deferred = $.Deferred(),
    xhr = $.ajax(…);
xhr.done(deferred.resolve).fail(deferred.reject).progress(deferred.notify);
但是,不鼓励这样使用,只要在需要时使用
xhr
,它们是相等的。或者使用
xhr.then()
创建一个全新的promise对象,其解析方式与
xhr
的解析方式完全相同

无论如何,上面代码的问题是,因为它返回一个函数,所以您可以随时执行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的函数,只是如果您快速连续多次调用它,它将中止最后一个请求,只完成最近一个请求


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

我认为这是不可能的…为什么不使用jqXHR回调解析延迟的函数…
xhr.done(function(){deferred.resolveWith.apply(deferred,arguments);})。fail(function(){deferred.rejectWith.apply)(延迟,参数);})
出于好奇,为什么要进行这种设置?为什么不直接使用jqXHR对象,而不是创建一个新的
$。延迟的
?另外,在您的解决方案中,您希望最终解决延迟的问题还是不解决延迟的问题?@RocketHazmat:在这个特定的场景中,我基本上希望在不立即执行的情况下构建一个“ajax”对象。因此,我考虑返回一个延迟的,这样用户仍然可以将他们的事件绑定到它,然后当他们执行它时,它将运行ajax调用…如果您愿意,我将发布一个更具体的示例,我承认我没有充分考虑它:-)@RocketHazmat:我实际上是将它与去Bouncing结合使用。我去Bouncing大约300毫秒,但是如果AJAX需要很长时间才能返回,它仍然会触发两次。我可以延迟执行新的AJAX调用,直到最后一个调用返回,但这会提供较差的用户体验……调用可能会堆积起来,他们会等待一段时间等待最新结果的时间太长了。@RocketHazmat:我不知道你的
$如何。延迟的
示例将如何工作……你没有将任何东西绑定到
$。ajax
--只有该对象知道调用何时返回。我认为Arun是目前为止最好的解决方案。不,请不要这样做。你的方法已被破坏,你的
AjaxOne
应确定实例