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

时间:2015-12-07 21:26:36

标签: javascript jquery ajax jquery-deferred

我需要做一个主要的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调用之前执行。

我认为这只是引入Deferred / Promise概念的那种场景(“回调地狱”),但我一直在努力解决这个问题。如何构造这些不同的AJAX请求,以便代码执行等到ajaxFunction完成并返回mainresult以供后续使用?

2 个答案:

答案 0 :(得分:3)

  

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

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

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

如果您坚持使用回调,那么您可以通过完成回调来传达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(),因为在一个将被重复调用的函数中插入事件处理程序可能是错误的设计(因为它最终会创建多个相同的事件处理程序)。如果你确定它是正确的做法,你可以把它插回去,但对我来说这看起来不对。

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

如果ajaxFetchingFunction1()ajaxFetchingFunction2()都被修改为返回承诺,那么您可以执行以下操作:

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
        }
    });
});

答案 1 :(得分:2)

Promise使得处理多个ajax请求真的很微不足道,但是"部分形式的影响"在GUI设计上可能更具挑战性。你必须考虑以下事项:

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

我们假设(对我而言可能不是OP)我们还不知道所有这些问题的答案,但它们可以体现在两个函数中 - {{ 1}}和validateAsync(),两者都接受setState()参数。

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

此阶段唯一需要的真实假设是表单/部分的选择器。我们假设它/它们都有stage

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()

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; } ,需要jQuery插件setState()

.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()以反映您选择的用户体验。