如何按连续顺序执行多个链接的 $.post() 调用?

How to execute multiple, chained $.post() calls in consecutive order?

我正在努力实现的目标

我需要触发1到3个不同的$.post()请求,是1次调用、2次连续调用还是3次连续调用由一些基本用户selection决定。每个调用只能在上一个调用完全结束后才开始。

我正在处理 3 个简单的行为案例——用户在看到 "Checkbox 1," "Checkbox 2," 和一个 "Continue" 按钮时,选择

  1. Select 什么都没有,然后按 "Continue" 按钮,这会调用 '/remote.php',
  2. 用户选择仅 select "Checkbox 1" "Checkbox 2," 然后按下 "Continue" 按钮,调用 $.post() Function 1 绑定到复选框 1,或 $.post() Function 2 绑定到复选框 2,然后对 '/remote.php',
  3. 进行 XHR 调用
  4. 或者用户 select 都选中复选框 1 + 2,然后按继续,调用 $.post() Function 1,然后调用 $.post() Function 2,然后对 '/remote.php'.

我需要确保继续按钮 $.post() 函数不会触发,直到复选框绑定 $.post() 函数触发并完成。


问题

问题是,如果 Checkbox 1 selected 和 Checkbox 2 selected,然后按下 Continue 按钮,据我了解,加载顺序应该是:

  1. 复选框 1 绑定 $.post() 请求触发并完成,然后
  2. 复选框 2 绑定 $.post() 请求触发并完成,然后
  3. 继续按钮绑定 $.post() 触发,并在绑定到 "Continue" 按钮绑定函数的函数末尾通过 AJAX 更改页面。

所以,结果应该是这样的:

XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/cart.php?action=add&product_id=1284". 
XHR finished loading: POST "/remote.php".

反而经常这样出来:

XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284". 

因此,当页面因 "last"/" 继续按钮功能末尾的 AJAX 而发生变化时,复选框 1 或复选框 2 操作均未发生,或者两者之一或两者之一确实在后端注册(即添加到购物车),但没有反映在 AJAXified DOM 中,因为它们应该作为最终的 AJAX 触发并在之前完成之前的 $.post() 调用已经完成。


我的代码

HTML

HTML基础:

<form method="post" action="#" onsubmit="newChooseShippingProvider(); return false;">
    <label for="delSigCheck" class="del-sig-text"><input id="delSigCheck" type="checkbox" onchange="addDelSigToCart();" title="Add Delivery Signature"></label>
    <label for="addInsCheck" class="ins-add-calc"><input id="addInsCheck" type="checkbox" onchange="addInsToCart();" title="Add Delivery Signature" data-ins-id="1284"></label>
    <input type="submit" value="Continue" class="btn Small">
</form>

Javascript/jQuery

这是我最近的--第四次或第五次--尝试,但仍然无效:

function addDelSigToCart() {
    $('#delSigCheck').toggleClass('checked');
}
function addInsToCart() {
    $('#addInsCheck').toggleClass('checked');
}
function newChooseShippingProvider() {
    var originalCheckout = ExpressCheckout.ChooseShippingProvider();
    if ($('.ShippingProviderList .radio span').hasClass('checked')) {
        var addInsCheck = $('#addInsCheck').hasClass('checked');
        var delSigCheck = $('#delSigCheck').hasClass('checked');
        var insId = $('#addInsCheck').attr('data-ins-id');
        var addDelSigUrl = '/cart.php?action=add&product_id=1280';
        var addInsUrl = '/cart.php?action=add&product_id=' + insId;
        if (delSigCheck && addInsCheck) {
            $.post(addDelSigUrl, function() {
                $.post(addInsUrl, function() {
                    originalCheckout;
                });
            });
        } else if (!delSigCheck && !addInsCheck) {
            originalCheckout;
        } else if (delSigCheck && !addInsCheck) {
            $.post(addDelSigUrl, function() {
                originalCheckout;
            });
        } else if (!delSigCheck && addInsCheck) {
            $.post(addInsUrl, function() {
                originalCheckout;
            });
        }
    } else {
        originalCheckout;
    }

我试过的

我已经经历了几个链接 $.post() 调用的版本,但似乎没有什么能始终如一地工作。

我现在使用的以及经过大量测试似乎对我最有效的方法是使用 setTimeout 来延迟链接函数,如下所示:

    ...
    if (delSigCheck && addInsCheck) {
        $.post(addDelSigUrl);
        setTimeout(function() {
            $.post(addInsUrl);
            setTimeout(function() {
                ExpressCheckout.ChooseShippingProvider();
            }, 1300);
        }, 1300);
    } else if ...

上面的这个版本是我现在使用的,因为它似乎给出了最一致的结果,看到脚本加载通常为 1,2,3,然后是 DOM AJAX 根据功能 1 和 2 进行适当更改。但是,我认为 setTimeout 无法正常工作,因为即使我将其增加到 5000 或 10000,也会执行操作 "instantaneously" 并且不会发生延迟(至少肯定不会接近 5-10 秒)。

我也试过将函数放在 $.post() 的成功回调中:

    ...
    if (delSigCheck && addInsCheck) {
        $.post(addDelSigUrl, function() {
            setTimeout(function() {
                $.post(addInsUrl, function() {
                    setTimeout(function() {
                        originalCheckout;
                    }, 1300);
                });
            }, 1300);
        });
    } else if ...

最后我也试过了:

$.when($.post(addDelSigUrl)).then(originalCheckout);

以及 .donesuccess: 但其中 none 有效,并且 $.posts() 以意外的顺序加载,失败.

问题

  1. 我做错了什么?
  2. 我怎样才能做到 1 次装满,然后 2 次装满,然后才 3 次点火和装填?

更新 1:

我刚试过 :

            $.post(addDelSigUrl, { cache: false }).then(function(data1) {
                //CONSOLE.LOGing HERE
                console.log(data1);
                return $.post(addInsUrl, { cache: false });
            }).then(function(data2) {
                //CONSOLE.LOGing HERE
                console.log(data2);
                return originalCheckout;
            });

但结果仍然是:

XHR finished loading: POST "/cart.php?action=add&product_id=1280".
XHR finished loading: POST "/remote.php".
XHR finished loading: POST "/cart.php?action=add&product_id=1284". 

并且两个 console.logs 在第一个 "XHR ..." 之后立即触发,然后 /remote.php 触发(尽管它应该作为 originalCheckout 的一部分最后触发),然后第三个 XHR 触发。

更新 2

现在我们通过 .then() 以正确的顺序触发和加载 XHR,我遇到的第二部分问题是 /remote.php 的第三个 XHR 更新了 DOM 通过 AJAX 使用来自后端的数据。该数据的一部分是第 1 和第 2 $.posts

我认为第 3 个 AJAX 调用在通过服务器端 PHP 在后端采取某些操作之前触发并完成几毫秒,因此超过 50% 的时间,通过第三次 AJAX 调用的 DOM 更新丢失了第一次 and/or 第二次调用的数据(通常 DOM 更改包括 Checkbox 1/AJAX 调用1,但不是 2)。

我该如何解决这个问题?我已经尝试过 setTimeout 但它似乎无法正常工作,即使我将它设置为 30000,第 3 个 AJAX 在第 1 个/第 2 个完成后立即触发。

最新前端代码:

function newChooseShippingProvider() {
    if ($('.ShippingProviderList .radio span').hasClass('checked')) {
        var addInsCheck = $('#addInsCheck').hasClass('checked');
        var delSigCheck = $('#delSigCheck').hasClass('checked');
        var insId = $('#addInsCheck').attr('data-ins-id');
        var addDelSigUrl = '/cart.php?action=add&product_id=1280';
        var addInsUrl = '/cart.php?action=add&product_id=' + insId;
        if (delSigCheck && addInsCheck) {
            $.post(addDelSigUrl).then(function(data1) {
                return $.post(addInsUrl);
            }).then(function(data2) {
                return ExpressCheckout.ChooseShippingProvider();
            });
        } else if (!delSigCheck && !addInsCheck) {
            ExpressCheckout.ChooseShippingProvider();
        } else if (delSigCheck && !addInsCheck) {
            $.post(addDelSigUrl).then(function(data1) {
                return ExpressCheckout.ChooseShippingProvider();
            });
        } else if (!delSigCheck && addInsCheck) {
            $.post(addInsUrl).then(function(data1) {
                return ExpressCheckout.ChooseShippingProvider();
            });
        }
    } else {
        ExpressCheckout.ChooseShippingProvider();
    }
}

如果您 "chain" AJAX post 操作(意味着您在收到上一次调用的数据后执行您的过程),那么不会发生任何奇怪的事情。

从症状我会想到更多与缓存相关的问题。向查询添加一个额外的随机值是一种快速而肮脏的方式,可以摆脱缓存结果和响应而不是应该缓存结果的人。此外,使用 POST 请求而不是 GET(如果可能)可能有助于解决此问题,并更好地传达操作是不应跳过或乱序执行的突变的想法。

请注意,过时的响应问题可能出现在多个级别:浏览器、代理、Web 服务器、cms 插件...

排序 jQuery ajax 操作的最简单方法是使用内置承诺:

$.post(...).then(function(data1) {
    return $.post(...);
}).then(function(data2) {
    return $.post(...);
}).then(function(data3) {
    // everything done here
});

向您展示精确排序的工作演示:http://jsfiddle.net/jfriend00/zcfr2xy0/


好的,看来问题出在您这样做:

var originalCheckout = ExpressCheckout.ChooseShippingProvider();

然后您认为稍后您可以这样做:

originalCheckout;

这将以某种方式执行前者。事实并非如此。您的 ExpressCheckout.ChooseShippingProvider() 函数会立即执行,并且执行该函数的 return 结果将分配给 originalCheckout

你根本不能那样做。当函数名称后有 () 时,这意味着立即执行它。我建议您只需将 originalCheckout; 的所有实例替换为 ExpressCheckout.ChooseShippingProvider();.