可以取消jQuery延期?
我有一种情况,我想取消延期。 延迟与ajax调用相关联。
为什么我使用延期
我不使用$ .ajax返回的正常的xhr对象。 我正在使用jsonp,这意味着我不能使用HTTP状态码进行error handling,并且必须将它们embedded到响应中。 然后检查代码,并将相关的延期对象标记为已解决或相应地拒绝。 我有一个自定义的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; }
为什么我要取消延期
有一个input字段用作列表的filter,并在input结束后半秒钟自动更新列表。 因为一次可能发生两个Ajax调用,所以我需要取消先前的调用,以确保它在第二个调用之后不会返回并显示旧数据。
我不喜欢的解决scheme
- 我不想拒绝延期,因为那会触发附加
.fail()
处理程序。 - 我不能忽略它,因为它会自动被标记为已解决或在Ajax返回时被拒绝。
- 删除延期将导致ajax调用返回时出现错误,并试图将延期标记为已解决或拒绝。
我该怎么办?
有没有办法取消推迟或删除任何附加的处理程序?
build议如何解决我的devise是受欢迎的,但是我们会优先考虑find一种方法去除处理程序或防止它们被触发。
查看jQuery文档和代码,我没有看到任何方式取消推迟的jQuery。
相反,您可能需要在您的resolveWith
处理程序中使用一种方法来知道后续的ajax调用已经被触发,并且此ajax调用应该忽略其结果。 你可以用一个全局递增的计数器来做到这一点。 在你的ajax调用开始时,你递增计数器,然后你把值转换成一个局部variables,或者把它作为ajax对象的一个属性。 在您的resolveWith
处理程序中,您检查计数器是否仍然具有与您的ajax调用开始时相同的值。 如果不是,则忽略结果。 如果是这样,没有新的Ajax调用被触发,所以你可以处理结果。
另外,你也可以拒绝在飞机上开启新的ajax呼叫,所以你一次只能有一个以上的飞行。 完成后,您可以使用该结果,或者根据需要启动下一个。
虽然你不能像你想要的那样“取消”延期,但是你可以创build一个简单的闭包来跟踪最后的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上 。 如果你想testing它。 设置超时与jsfiddles延迟器结合使用来模拟正在中断的呼叫。 您需要启用控制台的浏览器来查看日志。
在附注上,将任何.success(),.error()和complete()方法切换到延迟方法done(),fail()和always()。 通过jquery / ajax
弃用通知:在jQuery 1.8中,jqXHR.success(),jqXHR.error()和jqXHR.complete()callback将被弃用。 要准备代码以便最终删除,请使用jqXHR.done(),jqXHR.fail()和jqXHR.always()作为较新的代码
JustinY:好像你已经非常接近你想要的东西了。 你已经使用了两个延期(inner-> ajax和outer – > $ .Deferred())。 然后你使用内部延迟来决定如何解决外部延迟基于一些条件。
那么,当你不想要(也许你有一个布尔variables作为允许内部dfdparsing/拒绝的切换门)的时候,根本就不要parsing外部延迟。 没有什么不好的事情会发生:你连接到整个函数的任何处理程序都不会触发。 你内心成功的例子:
if(gateOpen){ gateOpen = false; if(hasStatus(jsonReturn, 'code', 200)) { deferred.resolveWith(this, [jsonReturn]); } else { deferred.rejectWith(this, [jsonReturn]); } }
如果你想跟踪或取消其他请求在其他function中,你也可以这样做。 但基本的是,你不必解决或拒绝外延迟。 即使你不取消/放弃内在的一样,这与取消它是一样的。
我创build了一个可以无缝添加取消延迟对象和ajax请求的function。
简而言之,一旦延迟对象被取消,分辨率/拒绝被完全忽略,并且state
变为“取消”。
根据jQuery.com的说法,“一旦对象已经进入解决或拒绝状态,它就停留在这种状态。” 因此,一旦延迟对象被parsing或拒绝,尝试取消就会被忽略。
(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();