链接jQuery承诺中止

时间:2014-02-13 21:59:28

标签: javascript jquery ajax promise

我目前正在编写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而无法中止它。虽然上面的示例非常简单,但在我的实际代码中,在很多地方都有几层链接。

有解决方法吗?

5 个答案:

答案 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)

我的解决方案受到了所有

的启发

使用jQuery promises

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;

使用Q + jQuery承诺

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()时进行转移,那会好得多。