我目前正在编写API代码,其中包含多个图层,包含$ .ajax()调用。
一个要求是用户必须能够取消任何请求(例如,如果它花费的时间太长)。
通常这可以通过简单的方式实现,例如:
var jqXHR = $.ajax(..);
$(mycancelitem).click(function () {
jqXHR.abort();
});
但是我的代码看起来更像是这样:
function myapicall() {
var jqxhr = $.ajax(…);
var prms = def.then(function (result) {
// modify the result here
return result + 5;
});
return prms;
}
这里的问题是有人调用myapicall()
只获取jQuery.Promise而无法中止它。虽然上面的示例非常简单,但在我的实际代码中,在很多地方都有几层链接。
有解决方法吗?
答案 0 :(得分:3)
你可以返回一个同时具有jqXHR和promise
的对象function myapicall() {
var jqXHR = $.ajax(..);
var promise = jqXHR.then(function (result) {
// modify the result here
return result + 5;
});
return {jqXHR:jqXHR,promise:promise};
}
答案 1 :(得分:2)
我的解决方案受到了所有
的启发client.js
var self = this;
function search() {
if (self.xhr && self.xhr.state() === 'pending') self.xhr.abort();
var self.xhr = api.flights(params); // Store xhr request for possibility to abort it in next search() call
self.xhr.then(function(res) {
// Show results
});
}
// repeatedly call search()
api.js
var deferred = new $.Deferred();
var xhr = $.ajax({ });
xhr.then(function (res) { // Important, do not assign and call .then on same line
var processed = processResponse(res);
deferred.resolve(processed);
});
var promise = deferred.promise();
promise.abort = function() {
xhr.abort();
deferred.reject();
};
return promise;
client.js
var self = this;
function search() {
if (self.xhr && self.xhr.isPending()) self.xhr.abort();
var self.xhr = api.flights(params); // Store xhr request for possibility to abort it in next search() call
self.xhr.then(function(res) {
// Show results
});
}
// repeatedly call search()
api.js
var deferred = Q.defer();
var xhr = $.ajax({ });
xhr.then(function (res) { // Important, do not assign and call .then on same line
var processed = processResponse(res);
deferred.resolve(processed);
});
deferred.promise.abort = function() {
xhr.abort();
deferred.reject();
};
return deferred.promise;
答案 2 :(得分:1)
基本上,你有自己的承诺,它将代表整个操作,并为它添加一个特殊的中止功能。如下所示:
function myapicall() {
var currentAjax = $.ajax({ ... })
.then(function(data) {
...
return currentAjax = $.ajax({ ... });
},
function(reason) { wrapper.reject(reason); })
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(...)
.then(function(data) {
...
wrapper.resolve(data);
},
function(reason) { wrapper.reject(reason); });
// haven't used jQuery promises, not sure if this is right
var wrapper = new $.Deferred();
wrapper.promise.abort = function() {
currentAjax.abort();
wrapper.reject('aborted');
};
return wrapper.promise;
}
此模式(更新currentAjax
变量)必须在$.ajax
链的每个阶段继续。在最后一次加载所有内容的AJAX调用中,您将使用您希望的任何数据来解析包装器承诺。
答案 3 :(得分:1)
寻找类似问题的解决方案,并解决了这个问题:
var xhr = $.ajax({ });
return xhr.then(function (res) {
var processed = processResponse(res);
return $.Deferred().resolve(processed).promise();
}).promise(xhr); // this is it. Extends xhr with new promise
这将返回带有中止的标准jqXHR对象以及所有+所有promise函数。
请注意,您可能也可能不想从.then()返回.promise(xhr)。取决于您希望如何处理来自API的.done()函数中的响应。
答案 4 :(得分:0)
我使用一组三个助手来解决这个问题。它几乎是手动的,您必须注意,不要忘记在API的实现中使用帮助程序,否则......中止不会中止。
function abort(prom) {
if( prom["abort"] ) {
prom["abort"]();
} else {
throw new Error("Not abortable");
}
}
function transfer_abort_from_one(abortable, other) {
other["abort"] = function() { abortable.abort() };
}
function transfer_abort_from_many(abortables, other) {
other["abort"] = function() {
for(let p of abortables) {
if(p["abort"]) {
p["abort"]();
}
}
};
}
现在
function api_call() {
let xhr = $.ajax(...);
let def = $.Deferred();
// along these lines
xhr.fail( def.reject );
xhr.done( def.resolve );
// then
let prom = def.promise();
transfer_abort_from_one(xhr, prom);
return prom;
}
function api_call2() {
let xhrs = [$.ajax(...), $.ajax(...)];
let prom = $.when.apply(null, xhrs);
transfer_abort_from_many(xhrs, prom);
return prom;
}
使用您的API并中止:
var api_prom = api_call();
abort(api_prom);
var api_prom2 = api_call2();
abort(api_prom2);
请注意,根据API的构建方式,您不能忘记将较低层承诺的中止转移到较高层的承诺。所有这些都容易出错。如果JQuery(例如)在调用when()
或then()
时进行转移,那会好得多。