Ajaxでsuccessした時に、さらにAjax通信したいです


アンケートを作っています。  
`<input type="button">`が複数あり、ボタンを押すとAjaxで親のフォームアクションから非同期通信を行います。

この時ボタンがA,B,C,Dあったとして、ABCは普通にAjax通信します。DだけABC同様に通信した後、さらに選択ボタンD1,D2,D3が現れ、それらもAjax通信する際、

[code]

    $.ajax({
        type: 'post',
        url: 'europost.jp/question.php',
        data: formdata
    }).done(function(data, status) {
        if(status == "success") {
            if($(this).hasClass('D') {
                    $.ajax({~省略
            }
            return false;
        }
    
[/code]

このようにAjax文の中にさらにAjax文をネストするのは嫌です。  
条件として、Dをクリックしても必ず最初に一度通信は行われるものです。  
できれば下記みたいなAjax関数にするのも嫌です。

[code]

    function Ajax(){
        if(D){
            Ajax();
        }
    }
    
[/code]




A,B,Cのボタンを押した時はbuttonTask, Dのボタンを押した時はbuttonTask, buttonDTaskを実行します。

GET/POST、フォーム/JSONなど細かな違いはありますが、本質的な部分はご要望を満たしているのではないかと思います。

[code]

    <html>
      <head>
        <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
        <script>
          $(function() {
            $.extend({
              sequence: function() {
                var tasks = Array.prototype.slice.call(arguments),
                    d = $.Deferred(),
                    p = d.promise();
                $.each(tasks, function(i, task) {
                  p = p.pipe(task);
                });
                d.resolve();
                return p;
              }
            });
    
            var buttonTask = function() {
              return $.getJSON('http://ip.jsontest.com').pipe(
                function(res) { return res; },
                function() { return 'button task failed!'; }
              );
            };
    
            var buttonDTask = function() {
              return $.getJSON('http://date.jsontest.com').pipe(
                function(res) { return res; },
                function() { return 'button D task failed!'; }
              );
            };
    
            $('button').on('click', function() {
              var s;
              if ($(this).hasClass('D')) {
                s = $.sequence(buttonTask, buttonDTask);
              } else {
                s = $.sequence(buttonTask);
              }
              s.fail(function(r) {
                console.log('fail: ', r);
              }).always(function(r) {
                console.log('always: ', r);
              });
            });
          });
        </script>
      </head>
      <body>
        <button>A</button>
        <button>B</button>
        <button>C</button>
        <button class="D">D</button>
      </body>
    </html>
    
[/code]




非同期処理で頻発するそのようなコールバックのネストを解消するためには、[ジェネレータ](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Iterators_and_Generators)を使います。コード例としては以下のようになります。

[code]

    co(function*() {
      
      var data = yield $.ajax({
        type: 'post',
        url: 'europost.jp/question.php',
        data: formdata
      }).promise();
    
      if ($(this).hasClass('D')) {
        var result = yield $.ajax({ ... }).promise();
    
        ...
        
        return false;
      }
    
    });
[/code]

[code]

    <script src="https://cdnjs.cloudflare.com/ajax/libs/co/4.1.0/index.js"></script>
[/code]

なお、PromiseやDeferredでコールバックのネストが避けられるという主張がされることがありますが、それは関数を分割してネストを分解しているだけで、今度は一箇所に本来まとまっているべきコードが分散するという別の問題を持ち込むことになり、根本的な問題の解決にはなりません。よく勘違いされますが、Promiseの目的は非同期処理の抽象化であって、コールバック地獄の解消ではありません。

