【问题标题】:Handle Ajax responses in same order as Ajax requests以与 Ajax 请求相同的顺序处理 Ajax 响应
【发布时间】:2015-03-17 19:52:31
【问题描述】:

我想保护我的代码,以确保由用户按特定顺序触发的多个 Ajax 请求将按相同顺序处理它们的响应。

我想保留 Ajax 的异步机制。只需保护我的代码,以避免可能导致网页混乱的乱序响应。

为了完全清楚。举个例子:

$('#button1').click(function(){
  $.ajax({url: 'dosomething1.html'})
    .done( function(){ console.log('something 1 success'); } )
    .fail( function(){ console.log('something 1 failure'); } );
});

$('#button2').click(function(){
  $.ajax({url: 'dosomething2.html'})
    .done( function(){ console.log('something 2 success'); } )
    .fail( function(){ console.log('something 2 failure'); } );
});

$('#button3').click(function(){
  $.ajax({url: 'dosomething3.html'})
    .done( function(){ console.log('something 3 success'); } )
    .fail( function(){ console.log('something 3 failure'); } );
});

如果用户点击“#button1”然后点击“#button2”然后点击“button3”,我想在控制台中看到:

>something 1 success
>something 2 success
>something 3 success

可能会发生响应未按照服务器发送它们的顺序接收的情况。所以我想为这种情况做好准备。

注意:我无法提前知道用户触发的事件顺序。所以我需要找到一种方法来“即时”链接响应处理程序。

实现这一目标的最佳解决方案是什么?

我是 Ajax 棘手问题的新手,我今天阅读了大量内容但没有找到解决方案(我猜想以某种方式延迟和承诺对象可以解决问题)。 请帮助我摆脱这种可怕的头痛。 :)


编辑以评论 Kevin B 的解决方案。

我绞尽脑汁想完全理解 Kevin B 的示例(确实有效),直到我阅读了一本关于 Deferreds 的书,该书解释了“then”函数实际上是在创建一个新的 Deferred 并返回它的承诺。

这是与前一个“链接”的新承诺。它根据之前的 Promise 评估结果(已解决或已拒绝)调用其成功的失败回调。

在我们的例子中,这意味着当前一个 promise 被评估时,“then”promise 也会被评估,并将前一个 promise 的结果(解决或拒绝)作为输入来决定调用哪个回调。 在 kevin B 的代码中,ajax 请求承诺在两种情况下都会返回(已解决或被拒绝)。 因此,只有在评估“then”promise 并且返回的 promise(ajax 请求一个)被“resolved”(.done 函数)或被拒绝(.fail 函数)时,才会调用 promise 的 .fail 和 .done 回调。

更进一步: 我的理解是,promise 是对未来可能发生的事件的一种监听器。

在经典情况下,当事件发生时,deferred 将更改为“已解决”或“已拒绝”状态并调用 promise 回调。 承诺是“监听”要更改的 deferred 的状态。触发此状态更改的事件是初始事件(ajax 请求、超时、其他...)的解决或拒绝。

在“then”的情况下,评估 promise 的触发事件是:引用的 promise(链中的前一个 promise)被评估(解决或拒绝)。 给定评估结果,调用成功或失败回调。

我提出这个受凯文代码启发的稍微重新组织的代码,以帮助像我这样的傻瓜更好地理解:

var currentPromise = $.Deferred().resolve().promise();

$('#button1').click(function(){

    var button1Promise = $.ajax({url: 'dosomething1.html'})

    var thenPromise = currentPromise.then(
        function () { return button1Promise;}, 
        function () { return button1Promise;}); // to also handle fail conditions gracefully

    currentPromise = thenPromise;

    // "thenPromise" callback functions are returning the promise linked to 
    // the ajax request. So this promise is "replacing" the "thenPromise". 
    // Hence "done" and "fail" functions are finally defined for the ajax request promise.
    thenPromise.done( function(){ console.log('something 1 success'); } );
    thenPromise.fail( function(){ console.log('something 1 failure'); } );
});

希望它能帮助那些对 jquery 概念不太熟悉的人完全理解带有“then”函数的 Promise 链接。

如果我误解了什么,请随时发表评论。

【问题讨论】:

  • 您能否在处理一个请求时禁用其他按钮?例如,按下按钮 1,禁用按钮 2,禁用按钮 3。然后,在完成或失败时,重新启用所有按钮。
  • 不,因为我不想阻止用户执行操作。不仅发送了 Ajax 请求。 DOM 的一些对象立即被修改。
  • 1 个简单的选项,即让 UI 具有 3 个响应区域并在它们到来时填充它们...因为 async 表示第一次调用可能需要 20 秒来自服务器,而第二次和第三次调用可能只需要 40 毫秒...没有像你说的那样阻塞按钮,最简单的方法是在 UI 中为 3 个响应留出空间,例如 <div class="resp-1"></div><div class="resp-2"></div><div class="resp-3"></div>,并在收到响应时将其填满...
  • 好的,这只是一个例子。不要专注于它。 :) 我真的需要以正确的顺序处理响应。黑客无法解决问题。
  • 理想情况下,当您想尽快发送请求但仍按发送顺序获取请求时,您应该使用 $.when,但是,在这种情况下 $.when 将不起作用因为你不知道你发送了多少,因为它是由用户点击触发的。您应该能够预先创建一个承诺,然后继续将请求链接到它,但这也不理想,因为您一次只能发送 1 个请求。

标签: javascript jquery ajax


【解决方案1】:

如果你预先创建了一个承诺,你可以继续链接它以获得你想要的效果。

// ajax mock
function sendRequest(v, delay) {
    var def = $.Deferred();
    setTimeout(function () {
        def.resolve(v);
    }, delay);
    return def.promise();
}

var ajaxPromise = $.Deferred().resolve().promise();
var delay = 600; // will decrement this with each use to simulate later requests finishing sooner

// think of this as a click event handler
function doAction (btnName) {
    delay -= 100;
    var promise = sendRequest(btnName, delay);
    ajaxPromise = ajaxPromise.then(function () {
        return promise;
    }).done(function () {
        console.log(btnName);
    });
}

doAction("1");
doAction("2");
doAction("3");
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

由于我将它们延迟了 500 毫秒、400 毫秒和 300 毫秒,因此直到 500 毫秒之后才记录它们。

这是您使用相同技术的代码:

var ajaxPromise = $.Deferred().resolve().promise();
$('#button1').click(function(){
  var promise = $.ajax({url: 'dosomething1.html'})

  ajaxPromise = ajaxPromise.then(function () {
    return promise;
  }, function () {
    return promise; // to also handle fail conditions gracefully
  }).done( function(){ console.log('something 1 success'); } )
    .fail( function(){ console.log('something 1 failure'); } );
});

// repeat for other two buttons

重要的是所有的 ajax 请求总是会立即发送,但 done 和 fail 处理程序直到轮到它们才会被执行。

【讨论】:

  • 哦,谢谢。我重新阅读了 jquery 文档,通过您的示例,一切都变得一清二楚。我很接近,但对这个概念还不够熟悉,无法像你那样写逻辑。非常感谢。
【解决方案2】:

我无法提前知道用户触发的事件顺序。 所以我需要找到一种“即时”链接响应处理程序的方法。

您需要管道 ajax 请求以使相应的响应以相同的顺序进行,一种方法是使用此插件 https://code.google.com/p/jquery-ajaxq/

【讨论】:

  • 我不想对请求进行排队,我想确保以与请求相同的顺序处理答案,以防它们在网络上采用不同的路线并且到达不正确订购。
  • 因此排队响应(而不是请求)可能是预期的效果。排队的请求会产生类似的效果,但会浪费时间。
【解决方案3】:

如你所说:

我无法提前知道用户触发的事件顺序。所以我需要找到一种方法来“即时”链接响应处理程序。

解决这个问题的正确方法绝对是使用延迟对象/承诺,而不是将 async 参数设置为 false,这可能会导致很多不必要的问题。

阅读有关如何操作的规范介绍here

编辑:

用$.when()同步并行任务的例子,取自here

var promiseOne, promiseTwo, handleSuccess, handleFailure;

// Promises
promiseOne = $.ajax({ url: '../test.html' });
promiseTwo = $.ajax({ url: '../test.html' });


// Success callbacks
// .done() will only run if the promise is successfully resolved
promiseOne.done(function () {
   console.log('PromiseOne Done');
});

promiseTwo.done(function () {
    console.log('PromiseTwo Done');
});

// $.when() creates a new promise which will be:
// resolved if both promises inside are resolved
// rejected if one of the promises fails
$.when(
   promiseOne,
   promiseTwo
)
.done(function () {
   console.log('promiseOne and promiseTwo are done');
})
.fail(function () {
   console.log('One of our promises failed');
});

【讨论】:

  • 好的,我很快就阅读了您的参考资料。我需要更深入地研究它,但我认为它对解决我的问题没有用处。我正在考虑调用 deferred.then 函数之类的方法,但我认为我不能直接在 .ajax 调用返回的 jqXHR 对象上执行此操作。当您事先知道链接时, deferred.then 使用起来“很容易”。但这里取决于用户的操作...... .
【解决方案4】:

这里最简单的方法是对 $.AJAX() 使用async : false 参数,以确保您的请求一个接一个地运行。 http://api.jquery.com/jquery.ajax/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-14
    • 1970-01-01
    相关资源
    最近更新 更多