假设我有一个$.Deferred
和一个jqXHR
对象。有没有办法将绑定到延迟(然后,总是,完成,失败)的所有处理程序转移到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对象。
答案 0 :(得分:2)
我们说我有一个$ .Deferred和一个jqXHR对象。有没有办法将绑定到延迟(然后,总是,完成,失败)的所有处理程序转移到XHR对象(根据我的理解,它是Deferred的扩展)?
或多或少,是的,但不是你想象的方式。而不是"移动处理程序",您只需解决延迟的XHR延迟(具有处理程序)。这将使延迟采用ajax promise的状态 - 或者不是,因为jQuery不是Promise A+兼容的。因此,您需要手动将触发器作为处理程序:
var deferred = $.Deferred(),
xhr = $.ajax(…);
xhr.done(deferred.resolve).fail(deferred.reject).progress(deferred.notify);
但是,不建议使用此类用途,只需在需要xhr
的任何地方使用deferred
- 它们就是平等的。或者使用xhr.then()
创建一个全新的承诺对象,其解析方式与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的函数,除非你快速连续多次调用它,它将中止最后一个请求并且只完成最近的请求。
听起来非常像一个事件流。您将需要查看功能反应式编程!
答案 1 :(得分: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);
});
}
});
到目前为止似乎工作得很好......但它没有回答我原来的问题。
所以一个全面的答案是:你不能将回调从一个延迟复制到另一个。我尝试以各种方式制作延期副本,$.extend({}, myDeferred)
我无法解决任何问题。我认为你必须手动制作每种方法的副本并启动相应的回调,类似于我所做的。
你可以将回调传递给原来的延期,正如Arun在他的评论中所暗示的那样(尽管我认为他的语法有些偏差;根据the docs,你不需要.apply
; 'with'方法的目的是允许您设置上下文)。在我的情况下,我希望能够不止一次地触发这些方法,因此这对我的方案不起作用。
答案 2 :(得分: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对象保留在元素中。