我有我想要取消延迟的情况。 递延与Ajax调用相关。
为什么我使用deferreds
我不使用由$就恢复正常XHR对象。 我使用JSONP,这意味着我不能使用HTTP状态代码的错误处理,并有嵌入他们的答复。 然后,将代码进行检查并作为解决或相应地拒绝相关联的延迟的对象标记。 我有一个自定义的API函数,这是否对我来说。
function api(options) {
var url = settings('api') + options.url;
var deferred = $.Deferred(function(){
this.done(options.success);
this.fail(options.error);
});
$.ajax({
'url': url,
'dataType':'jsonp',
'data': (options.noAuth == true) ? options.data : $.extend(true, getAPICredentials(), options.data)
}).success(function(jsonReturn){
// Success
if(hasStatus(jsonReturn, 'code', 200)) {
deferred.resolveWith(this, [jsonReturn]);
}
// Failure
else {
deferred.rejectWith(this, [jsonReturn]);
}
});
return deferred;
}
我为什么要取消延期
有用作用于列表的过滤器和打字结束后会自动更新列表半秒的输入栏。 因为它是可能两个Ajax调用是优秀的时间,我需要取消以前的通话,以确保它不会之后的第二回,并显示旧数据。
解决方案我不喜欢
- 我不想拒绝延期,因为这将触发附有处理
.fail()
- 我不能忽视它因为解决它会自动标记或AJAX返回时被拒绝。
- 当AJAX调用返回,并试图标记为递延解决或拒绝删除推迟将导致错误。
我该怎么办?
有没有办法取消延迟或删除任何附加的处理程序?
关于如何解决我的设计是值得欢迎的,但偏好作出建议,以寻找一种方法来移除处理程序或阻止他们射击。
展望jQuery的文档和代码,我看不出有什么办法取消推迟一个jQuery。
相反,你可能需要一种方式,你resolveWith
处理知道后续的Ajax调用已经被解雇,这Ajax调用应该忽略它的结果。 你可以做到这一点与全球递增计数器。 在你的Ajax调用的开始,你增加计数器,然后你抢值到一个局部变量或把它作为Ajax对象上的属性。 在你resolveWith
处理程序中,检查是否计数器仍具有相同的值,当你的Ajax调用开始。 如果没有,你忽略的结果。 如果是这样,没有新的Ajax调用已经被解雇,因此您可以处理结果。
或者,你可以拒绝触发一个新的Ajax调用,而一个是在飞行已经让你永远在一次飞行中有一个以上的。 当一个完成后,您既可以只使用该结果,或者如果需要触发下一个。
虽然像你想你可以不是“取消”一延迟,你可以创建一个简单的闭合通过$就跟踪最后Ajax调用的返回一个jqXHR对象。 通过这样做,你可以简单地中止()的调用时,一个新的jqXHR也发挥了作用,如果最后一个没有完成。 在你的代码的情况下,它会拒绝jqXHR和离开延迟开放,你最初想将其删除。
var api = (function() {
var jqXHR = null;
return function(options) {
var url = options.url;
if (jqXHR && jqXHR.state() === 'pending') {
//Calls any error / fail callbacks of jqXHR
jqXHR.abort();
}
var deferred = $.Deferred(function() {
this.done(options.success);
this.fail(options.error);
});
jqXHR = $.ajax({
url: url,
data: options.toSend,
dataType: 'jsonp'
});
jqXHR.done(function(data, textStatus, jqXHR) {
if (data.f && data.f !== "false") {
deferred.resolve();
} else {
deferred.reject();
}
});
//http://api.jquery.com/deferred.promise/
//keeps deferred's state from being changed outside this scope
return deferred.promise();
};
})();
我已经张贴此的jsfiddle 。 如果你想测试一下。 设置超时结合使用jsfiddles延迟器来模拟被所中断通话。 你需要一台启用浏览器查看日志。
在一个侧面说明进行任意切换.success(),.error(),并完成()方法到完成延期的方法(),失败(),始终()。 通过jQuery的/ AJAX
弃用通知:jqXHR.success(),jqXHR.error(),和jqXHR.complete()回调将在jQuery的1.8被弃用。 要为他们的最终准备拆除你的代码,使用jqXHR.done(),jqXHR.fail(),和jqXHR.always(),而不是作为新
JustinY:好像你真的已经接近你想要什么。 你已经使用两个deferreds(inner->阿贾克斯和外 - > $ .Deferred())。 然后,您使用的是内部推迟到决定如何解决外根据一些条件来推迟。
好了,所以才没有解决外推迟所有当你不希望(也许你有充当切换门调节内DFD解决/拒绝在所有的布尔变量)。 没有什么大不了的事情:你已经连接到这整个功能将不会触发任何处理。 例如,在你的内心成功的功能:
if(gateOpen){
gateOpen = false;
if(hasStatus(jsonReturn, 'code', 200)) {
deferred.resolveWith(this, [jsonReturn]);
}
else {
deferred.rejectWith(this, [jsonReturn]);
}
}
在应用程序中的一些其他逻辑将决定当gateOpen被重新设置为true(某种_.throttle()或_.debounce()超时,用户交互,任何你想要的)。如果你想跟踪或取消其他请求在该功能的其他人,你可以做到这一点。 但基本的事情是,你没有解决或拒绝外推迟。 而这一样取消它,即使你不取消/放弃内部之一。
我创建了一个垫片,可以无缝地添加到取消递延对象和Ajax请求的能力。
总之,一旦延期对象已被取消,分辨率/拒绝完全被忽视,而state
变为“取消”。
据jQuery.com,“一旦对象已经进入解决或拒绝状态,它停留在那种状态。” 因此,试图取消被忽略,一旦递延对象得到解决或者拒绝。
(function () {
originals = {
deferred: $.Deferred,
ajax: $.ajax
};
$.Deferred = function () {
var dfr = originals.deferred(),
cancel_dfr = originals.deferred();
dfr.canceled = false;
return {
cancel: function () {
if (dfr.state() == 'pending') {
dfr.canceled = true;
cancel_dfr.resolve.apply(this, arguments);
}
return this;
},
canceled: cancel_dfr.done,
resolve: function () {
if ( ! dfr.canceled) {
dfr.resolve.apply(dfr, arguments);
return this;
}
},
resolveWith: function () {
if ( ! dfr.canceled) {
dfr.resolveWith.apply(dfr, arguments);
return this;
}
},
reject: function () {
if ( ! dfr.canceled) {
dfr.reject.apply(dfr, arguments);
return this;
}
},
rejectWith: function () {
if ( ! dfr.canceled) {
dfr.rejectWith.apply(dfr, arguments);
return this;
}
},
notify: function () {
if ( ! dfr.canceled) {
dfr.notify.apply(dfr, arguments);
return this;
}
},
notifyWith: function () {
if ( ! dfr.canceled) {
dfr.notifyWith.apply(dfr, arguments);
return this;
}
},
state: function () {
if (dfr.canceled) {
return "canceled";
} else {
return dfr.state();
}
},
always : dfr.always,
then : dfr.then,
promise : dfr.promise,
pipe : dfr.pipe,
done : dfr.done,
fail : dfr.fail,
progress : dfr.progress
};
};
$.ajax = function () {
var dfr = $.Deferred(),
ajax_call = originals.ajax.apply(this, arguments)
.done(dfr.resolve)
.fail(dfr.reject),
newAjax = {},
ajax_keys = [
"getResponseHeader",
"getAllResponseHeaders",
"setRequestHeader",
"overrideMimeType",
"statusCode",
"abort"
],
dfr_keys = [
"always",
"pipe",
"progress",
"then",
"cancel",
"state",
"fail",
"promise",
"done",
"canceled"
];
_.forEach(ajax_keys, function (key) {
newAjax[key] = ajax_call[key];
});
_.forEach(dfr_keys, function (key) {
newAjax[key] = dfr[key];
});
newAjax.success = dfr.done;
newAjax.error = dfr.fail;
newAjax.complete = dfr.always;
Object.defineProperty(newAjax, 'readyState', {
enumerable: true,
get: function () {
return ajax_call.readyState;
},
set: function (val) {
ajax_call.readyState = val;
}
});
Object.defineProperty(newAjax, 'status', {
enumerable: true,
get: function () {
return ajax_call.status;
},
set: function (val) {
ajax_call.status = val;
}
});
Object.defineProperty(newAjax, 'statusText', {
enumerable: true,
get: function () {
return ajax_call.statusText;
},
set: function (val) {
ajax_call.statusText = val;
}
});
// canceling an ajax request should also abort the call
newAjax.canceled(ajax_call.abort);
return newAjax;
};
});
添加后,您可以取消一个Ajax调用:
var a = $.ajax({
url: '//example.com/service/'
});
a.cancel('the request was canceled');
// Now, any resolutions or rejections are ignored, and the network request is dropped.
..或简单的递延对象:
var dfr = $.Deferred();
dfr
.done(function () {
console.log('Done!');
})
.fail(function () {
console.log('Nope!');
});
dfr.cancel(); // Now, the lines below are ignored. No console logs will appear.
dfr.resolve();
dfr.reject();