强制jQuery延迟等到Ajax完成"然后"处理器

时间:2015-03-25 00:20:03

标签: javascript jquery ajax promise jquery-deferred

我认为我需要使用“then”处理程序创建一个Deferred对象,但是等到“then”处理程序完成它自己的承诺后再继续。

用例是一个记录对象,上面的函数是它的保存方法。记录对象有一个名为saveQueue的属性,在记录的实例化中设置为$ .Deferred()。对saveQueue的解析调用应该确保Deferred总是会尽可能快地执行附加到它的每个新处理程序。这个想法是你可以在短时间内在记录上多次调用save,但调用会一个接一个地运行,而不是重叠。

我正在使用Deferred来排队Ajax调用,因此在前一次调用完成之前不会运行。但是,从同一个方法,我想返回一个可以被jQuery Ajax对象解析/拒绝的Deferred,如下所示:

 record.saveQueue = $.Deferred();

 self.save = function( record ){
    var deferredAction = $.Deferred();

    deferredAction.then(function() {
        return $.post("/example_save_endpoint");
    });

    record.saveQueue.always(function(){
      deferredAction.resolve();
    }).resolve();

    return deferredAction;
  }

然而,当我使用这段代码时,deferredAction承诺总是以resolved结束,大概是因为#then处理程序返回一个“挂起”(因而是非拒绝)的承诺。在解决/拒绝之前,有没有办法强制Deferred等待Ajax承诺完成?或者是否有另一种更好的方法来穿针呢?

2 个答案:

答案 0 :(得分:0)

我可能会选择不这样做,但延迟/承诺确实可以用作排队设备。

你需要对你已经尝试的内容进行轻微的(?)变异。

self.queue = $.when();//A resolved promise, used to form a queue of functions in a .then() chain.

self.save = function(data) {
    var dfrd = $.Deferred();//A Deferred dedicated to this particular save.
    self.queue = self.queue.then(function() {
        return $.post("/example_save_endpoint", data) //Make the AJAX call, and return a jqXHR to ensure the downstream queue waits for this jqXHR to resolve/reject.
            .then(dfrd.resolve, dfrd.reject) //Resolve/reject the Deferred for the caller's benefit
            .then(null, function() {
                //Force failure down the success path to ensure the queue is not killed by an AJAX failure.
                return $.when();//Return a resolved promsie, for the queue's benefit.
            });
    });
    return dfrd.promise();//allow the caller to do something when the AJAX eventually responds
}

有关说明,请参阅代码中的注释

答案 1 :(得分:0)

您的想法可能有效,但

  • 每次调用该方法时都不能使用.resolve()解析队列,而应仅使用已解决的promise初始化该队列。
  • record.saveQueue上实际队列,需要在每次方法调用时更改(覆盖),以表示最新请求的结束。

我们don't need any deferredswe can work with the promises$.post返回。

所以使用这个:

var emptyQueue = $.when(undefined); // an already fulfilled promise as the start
// equivalent: = $.Deferred().resolve().promise();

function startQueue() {
    return emptyQueue; // yes, this delibaretely returns a constant, the begin
                       // of the queue always looks the same (and is never mutated)
}

// every time you create a record, do
record.saveQueue = startQueue();

// and use that in your methods:
this.save = function(record) {
    var queuedRequestResult = record.saveQueue.then(function() {
        return $.post("/example_save_endpoint");
//      ^^^^^^ promises chain :-)
    });
    // Magic happens here:
    record.saveQueue = queuedRequestResult // we swap the previous queue promise for a new
                                           // one that resolves only after the request
      .then(startQueue, startQueue);       // and make sure it then starts with a fresh
                                           // queue, especially when the request failed
    //.then(null, startQueue) is similar, except unnecessarily remembering the last result
    return queuedRequestResult;
}