如何在继续之前构建这些嵌套的异步请求以完成批处理

How to structure these nested asynchronous requests to complete a batch before proceeding?

本文关键字:请求 异步 批处理 嵌套 继续 构建      更新时间:2023-09-26

我需要做一个主要的AJAX表单提交。但是,我想在继续主要提交之前,中途执行一系列其他初步表单提交和 AJAX 请求。

下面是这个想法,但有很多伪代码。我想如图所示调用ajaxFunction,完成其所有任务,然后继续提交主表单:

$('#mainform').submit(function(e){
    e.preventDefault();
    var main = this;
    var data = $('#section :input', main).serialize();
    //preliminary nested ajax requests
    var mainresult = ajaxFunction('arg1', 'arg2');

    alert("All preliminary AJAX done, proceeding...");
    if(mainresult){     
        //final ajax
        $.post('mainurl', data, function(result){
            console.log(result);
        });
    }else{
        //do nothing
    }
});

function ajaxFunction(param1, param2){
    //ajax1
    ajaxFetchingFunction1('url1', function(){
        //ajax2
        ajaxFetchingFunction2('url2', function(){
            //submit handler
            $('#anotherform').submit(function(){
                if(someparam === 1){
                    return true;
                }else{
                    return false;
                }
            });
        });
    });

}

就像现在一样,我知道由于所有异步嵌套的 AJAX 调用,它不会按预期工作。我得到的是,alert("All preliminary AJAX done, proceeding...");甚至在ajaxFunction中的任何AJAX调用之前执行。

我相信这只是引入延期/承诺概念的那种场景("回调地狱"(,但我一直在努力解决这个问题。如何构建这些不同的 AJAX 请求,以便代码执行将等到ajaxFunction完成并返回mainresult以供后续使用?

如何构建这些不同的 AJAX 请求,以便代码 执行将等到 ajaxFunction 完成并返回 主要结果用于后续使用?

你不能,你也不会。 Javascript 不会"等待"异步操作完成。 相反,您将要在异步操作完成后运行的代码移动到回调中,然后在异步操作完成时调用该回调。 无论是使用纯异步回调还是作为承诺一部分的结构化回调,都是如此。

Javascript 中的异步编程需要重新思考和重构控制流,以便在异步操作完成后要运行的内容放入回调函数中,而不仅仅是在下一行代码上按顺序执行。 异步操作通过一系列回调按顺序链接。 承诺是简化这些回调管理的一种手段,特别是简化错误的传播和/或多个异步操作的同步。


如果您坚持使用回调,则可以使用完成回调来传达ajaxFunction()的完成:

function ajaxFunction(param1, param2, doneCallback){
    //ajax1
    ajaxFetchingFunction1('url1', function(){
        //ajax2
        ajaxFetchingFunction2('url2', function(){
            doneCallback(someResult);
        });
    });
}

然后,在此处使用它:

$('#mainform').submit(function(e){
    e.preventDefault();
    var main = this;
    var data = $('#section :input', main).serialize();
    //preliminary nested ajax requests
    ajaxFunction('arg1', 'arg2', function(result) {
        // process result here
        alert("All preliminary AJAX done, proceeding...");
        if(result){     
            //final ajax
            $.post('mainurl', data, function(result){
                console.log(result);
            });
        }else{
            //do nothing
        }
    });
});

注意:我从代码中删除了您的$('#anotherform').submit(),因为在将重复调用的函数中插入事件处理程序可能是错误的设计(因为它最终会创建多个相同的事件处理程序(。 如果你确定这是正确的做法,你可以把它插回去,但对我来说它看起来不对。


这通常是使用承诺的好地方,但你的代码有点抽象,无法准确地向你展示如何使用承诺。 我们需要看到 ajaxFetchingFunction1()ajaxFetchingFunction2() 的真实代码来说明如何使用 promise 来做到这一点,因为这些异步函数需要创建和返回 promise。 如果你在它们内部使用jQuery ajax,那么这将很容易,因为jQuery已经为ajax调用创建了一个承诺。

如果修改了 ajaxFetchingFunction1()ajaxFetchingFunction2() 以返回 promise,那么您可以执行以下操作:

function ajaxFunction(param1, param2){
    return ajaxFetchingFunction1('url1').then(function() {
        return ajaxFetchingFunction2('url2');
    });
}

然后,在此处使用它:

$('#mainform').submit(function(e){
    e.preventDefault();
    var main = this;
    var data = $('#section :input', main).serialize();
    //preliminary nested ajax requests
    ajaxFunction('arg1', 'arg2').then(function(result) {
        // process result here
        alert("All preliminary AJAX done, proceeding...");
        if(result){     
            //final ajax
            $.post('mainurl', data, function(result){
                console.log(result);
            });
        }else{
            //do nothing
        }
    });
});

承诺使处理多个 ajax 请求变得非常微不足道,但是"部分形式"对 GUI 设计的影响可能更具挑战性。您必须考虑以下事项:

  • 一种形式分为几个部分,还是每个部分一个形式?
  • 在开始时显示所有部分,还是逐步显示它们?
  • 锁定先前验证的部件以防止验证后进行干预?
  • 在每个阶段重新验证所有部件,还是仅重新验证当前部件?
  • 一个
  • 整体提交按钮还是每个部分一个提交按钮?
  • 应如何标记提交按钮(以帮助用户了解他参与的过程(?

让我们假设(就像我的情况一样,但可能不是 OP(我们还不知道所有这些问题的答案,但它们可以体现在两个函数中 - validateAsync()setState() ,两者都接受stage参数。

这使我们能够编写一个通用的主例程,以满足未知的验证调用和各种 GUI 设计决策。

此阶段唯一需要的真正假设是表单/部分的选择器。让我们假设它/他们都有class="partialForm"

$('.partialForm').on('submit', function(e) {
    e.preventDefault();
    $.when(setState(1)) // set the initial state, before any validation has occurred.
    .then(validateAsync.bind(null, 1)).then(setState.bind(null, 2))
    .then(validateAsync.bind(null, 2)).then(setState.bind(null, 3))
    .then(validateAsync.bind(null, 3)).then(setState.bind(null, 4))
    .then(function aggregateAndSubmit() {
        var allData = ....;  // here aggregate all three forms' into one serialization.
        $.post('mainurl', allData, function(result) {
            console.log(result);
        });
    }, function(error) {
        console.log('validation failed at stage: ' + error.message);
        // on screen message for user ...
        return $.when(); //inhibit .fail() handler below.
    })
    .fail(function(error) {
        console.log(error);
        // on screen message for user ...
    });
});

在这里调用setState()作为 then 回调在语法上很方便,尽管它(可能(是同步的

样本validateAsync()

function validateAsync(stage) {
    var data, jqXHR;
    switch(stage) {
        case 1: 
            data = $("#form1").serialize();
            jqXHR = $.ajax(...);
            break;
        case 2: 
            data = $("#form2").serialize();
            jqXHR = $.ajax(...);
            break;
        case 3: 
            data = $("#form3").serialize();
            jqXHR = $.ajax(...);
    }
    return jqXHR.then(null, function() {
        return new Error(stage);
    });
}

样本setState()

function setState(stage) {
    switch(stage) {
        case 1: //initial state, ready for input into form1
            $("#form1").disableForm(false);
            $("#form2").disableForm(true);
            $("#form3").disableForm(true);
        break;
        case 2: //form1 validated, ready for input into form2
            $("#form1").disableForm(true);
            $("#form2").disableForm(false);
            $("#form3").disableForm(true);
        break;
        case 3: //form1 and form2 validated, ready for input into form3
            $("#form1").disableForm(true);
            $("#form2").disableForm(true);
            $("#form3").disableForm(false);
        break;
        case 4: //form1, form2 and form3 validated, ready for final submission
            $("#form1").disableForm(true);
            $("#form2").disableForm(true);
            $("#form3").disableForm(true);
    }
    return stage;
}

setState()所述,将需要jQuery插件.disableForm()

jQuery.fn.disableForm = function(bool) {
    return this.each(function(i, form) {
        if(!$(form).is("form")) return true; // continue
        $(form.elements).each(function(i, el) {
            el.readOnly = bool;
        });
    });
}

正如我所说,上面的validateAsync()setState()只是基本的样本。至少,您需要:

  • 充实validateAsync()
  • 修改setState()以反映您选择的用户体验。