【问题标题】:Chain ajax and execute it in sequence. Jquery Deferred链ajax并按顺序执行。 Jquery 延迟
【发布时间】:2013-04-29 09:18:17
【问题描述】:

我有 3 个需要 ajax 来完成的进程。但它是异步的,它不能做我想做的事..

让我们说:

function a(param1, param2) {
     $.post(..., function(result){
         if(result){
            b();
         } else {
            console.log("failed a");
         }
     })
}

function b() {
      $.post(..., function(result){
         if(result){
            c();
         } else {
            console.log("failed b");
         }
     })
}

function c() {
     $.post(..., function(result){
         if(result){
            console.log("successful");
         } else {
            console.log("failed b");
         }
     })
}

我希望它像这样执行

a
b
c

如您所见,该代码将完美运行。 但如果使用循环。

 var data = [{param1 : 1235, param2: 3214},  {param1 : 5432, param2: 9876}];

 $.each(data, function(k,v){
      a(v.param1, v.param2)
 });

它不会按预期工作,只会做:

a
a
b
b
c
c

而不是

a
b
c
a
b
c

【问题讨论】:

  • 你的结构没有问题..你能提供更多关于代码的细节吗..
  • 是的,这并没有什么问题,但是如果你继续阅读,代码的执行如果循环是 aa,bb,cc 而不是 abc,abc
  • 所以你要跟踪哪个是首先被调用的......现在你把控制台只放在失败......
  • 实际上你想用 async: false 进行调用.. 但这是一个不好的做法..
  • Erm... 将异步设置为 false?

标签: javascript jquery ajax each deferred


【解决方案1】:

这是一个非常简单和高效的 AJAX 链接/队列插件。它将依次执行您的ajax方法。

它通过接受一组方法然后按顺序执行它们来工作。它在等待响应时不会执行下一个方法。

//--- 这部分是您的代码 -----------------------

$(document).ready(function () {

var AjaxQ = [];
AjaxQ[0] = function () { AjaxMethod1(); }
AjaxQ[1] = function () { AjaxMethod2(); }
AjaxQ[3] = function () { AjaxMethod3(); }

//Execute methods in sequence
$(document).sc_ExecuteAjaxQ({ fx: AjaxQ });

});

//--- 这部分是 AJAX 插件 -------------------

$.fn.sc_ExecuteAjaxQ = 函数(选项){

//? Executes a series of AJAX methods in dequence

var options = $.extend({

    fx: [] //function1 () { }, function2 () { }, function3 () { }

}, options);

if (options.fx.length > 0) {

    var i = 0;

    $(this).unbind('ajaxComplete');
    $(this).ajaxComplete(function () {

        i++;
        if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); }
        else { $(this).unbind('ajaxComplete'); }

    });

    //Execute first item in queue
    if (typeof options.fx[i] == "function") { options.fx[i](); }
    else { $(this).unbind('ajaxComplete'); }

} 

}

【讨论】:

    【解决方案2】:

    我看到 c 不依赖于 b 结果,并且 b 不依赖于 a 结果。

    遵循 GRASP 原则(http://en.wikipedia.org/wiki/GRASP_(object-oriented_design)a 不能知道 bb 不能知道 c。

    当我们编程时,记住 GRASP 原则或指南非常重要。

    高内聚和低耦合意味着我们的代码会更好、更可重用且更易于维护。

    知道 abc 的 main 函数必须构建链式调用。

    函数是:

        function a(param1, param2) {
    
            var deferred = $.Deferred();
    
            console.log("    function a: begin. Params " + param1 + " and " + param2);
    
            mockPost("a_url").done(function() {
                console.log("    function a: end ok. Params " + param1 + " and " + param2);
                deferred.resolve();
            }).fail(function() {
                console.log("    function a: end fail. Params " + param1 + " and " + param2);
                deferred.reject();
            });
    
            return deferred.promise();
        }
    
        function b() {
    
            var deferred = $.Deferred();
    
            console.log("    function b: begin");
    
            mockPost("b_url").done(function() {
                console.log("    function b: end ok.");
                deferred.resolve();
            }).fail(function() {
                console.log("    function b: end fail.");
                deferred.reject();
            });
    
            return deferred.promise();
        }
    
        function c() {
    
            // We suppose that c function calls to post function and anything more
            return mockPost("c_url");
        }
    

    主要功能是:

        // Array with params for a function (a function is the first link in chain)
        var data = [{param1 : 1235, param2: 3214},  {param1 : 5432, param2: 9876}];
    
        // Array with calls to each fixed sequence a, b, and c. We iterate over data array
        var arrayFunctions = [];
        $.each(data, function(i,obj) {
            arrayFunctions.push(
                function() {
                    console.log("Params in data with index " + i + ":");
    
                    // We define the fixed sequence: a with params, b without params and c without params 
                    return $.iterativeWhen( 
                        function() {
                            return a(obj.param1, obj.param2);
                        },
                        b,
                        c
                    );
                }
            )
        });
    
        // Start the global process
        $.iterativeWhen.apply($, arrayFunctions)
        .done(function() {
            console.log ("----------------");
            console.log ("> Global Success");       
        })
        .fail(function() {
            console.log ("--------------");
            console.log ("> Global Fail");
        });
    

    $.iterativeWhen 在 jQuery 中不存在,所以我构建了它。它适用于 jQuery 1.8 及更高版本。

    $.iterativeWhen = function () {
    
        var deferred = $.Deferred();
        var promise = deferred.promise();
    
        $.each(arguments, function(i, obj) {
    
            promise = promise.then(function() {
                return obj();
            });
        });
    
        deferred.resolve();
    
        return promise;
    };
    

    mockPost 函数以成功概率模拟对 $.post 的调用:

    function mockPost(url) {
    
            var deferred = $.Deferred();
    
            setTimeout(function() {
                if (Math.random() <= 0.9) {
                    console.log("        request url: " + url +  "... ok");
                    deferred.resolve();
                } else {
                    console.log("        request url: " + url +  "... fail");
                    deferred.reject();
                }
            }, 1000);
    
            return deferred.promise();
        }
    

    日志输出为:

    Params in data with index 0: 
        function a: begin. Params 1235 and 3214 
            request url: a_url... ok 
        function a: end ok. Params 1235 and 3214 
        function b: begin 
            request url: b_url... ok 
        function b: end ok. 
            request url: c_url... ok 
    Params in data with index 1: 
        function a: begin. Params 5432 and 9876
            request url: a_url... ok
        function a: end ok. Params 5432 and 9876 
        function b: begin 
            request url: b_url... ok 
        function b: end ok. 
            request url: c_url... ok 
    ---------------- 
    > Global Success 
    

    jsFiddle 在这里:http://jsfiddle.net/E2tp3/

    【讨论】:

      【解决方案3】:

      有很多方法可以写这种东西。

      一种灵活的方法是将“动作”与“序列”分开,允许:

      • 函数 a、b、c 启动异步 (ajax) 操作,但不知道如何对它们进行排序
      • a、b、c 可重复使用,作为一个或多个序列的一部分或根据需要单独使用。

      这是一种编码这种方法的方法,专门为链接逻辑使用.then()

      function a() {
          return $.post(...).then(function(result) {
              if(result)
                  return result;//continue on "success" path.
              else
                  return $.Deferred().reject('a').promise();//convert success to failure.
          }, function() {
              return 'a';//continue on failure path.
          });
      }
      function b() {
          return $.post(...).then(function(result) {
              if(result)
                  return result;//continue on "success" path.
              else
                  return $.Deferred().reject('b').promise();//convert success to failure.
          }, function() {
              return 'b';//continue on failure path.
          });
      }
      function c() {
          return $.post(...).then(function(result) {
              if(result)
                  return result;//continue on "success" path.
              else
                  return $.Deferred().reject('c').promise();//convert success to failure.
          }, function() {
              return 'c';//continue on failure path.
          });
      }
      
      a().then(b).then(c).then(function() {
          console.log("successful");
      }, function(id) {
          console.log("failed: " + id);
      });
      

      或者,如果您想在循环中调用单个异步函数a,那么代码可能是这样的:

      function a(obj) {
          return $.post(...).then(function(result) {
              if(result)
                  return result;//continue on "success" path.
              else
                  return $.Deferred().reject(obj.id).promise();//convert success to failure.
          }, function() {
              return obj.id;//continue on failure path.
          });
      }
      
      var data = [{id:'A', param1:1235, param2:3214},  {id:'B', param1:5432, param2:9876}];
      //Note how IDs are included so these data objects can be identified later in failure cases.
      
      var dfrd = $.Deferred();//starter Deferred for later resolution.
      var p = dfrd.promise();//A promise derived from the starter Deferred, forming the basis of a .then() chain.
      
      //Build a .then() chain by assignment
      $.each(data, function(i, obj) {
          p = p.then( function() {
              return a(obj);
          });//By not including a fail handler here, failures will pass straight through to be handled by the terminal .then()'s fail handler.
      });
      
      //Chain a terminal .then(), with success and fail handlers.
      p.then(function() {
          console.log("successful");
      }, function(id) {
          console.log("failed: " + id);
      });
      
      dfrd.resolve();//Resolve the starter Deferred to get things started.
      

      【讨论】:

      • 感谢延迟示例,正是我想要的!
      • 所以如果这是在一个函数中,你会返回dfrd 以便调用者可以解决它吗?或者你会在这里解决它并返回承诺,以便调用者可以附加 .then()s 到它?
      【解决方案4】:

      您可以使用 jQuery 的延迟对象和使用 'then' 来链接异步调用,例如 ajax 调用。

      您还可以将其更改为使用返回延迟承诺对象的函数,而不是像我在示例中那样使用 ajax 调用。

      http://jsfiddle.net/q4cFv/

      (异步函数示例:http://jsfiddle.net/q4cFv/1/

      $(function() {
          var delay = 3,
              span = $('span'),
              posts = [
                  {
                      input1: 'My name 1',
                      input2: 'My address 1',
                      input3: 'My country 1'
                  },
                  {
                      input1: 'My name 2',
                      input2: 'My address 2',
                      input3: 'My country 2'
                  },
                  {
                      input1: 'My name 3',
                      input2: 'My address 3',
                      input3: 'My country 3'
                  },
                  {
                      input1: 'My name 4',
                      input2: 'My address 4',
                      input3: 'My country 4'
                  }
              ],
              looper = $.Deferred().resolve();
      
          $.each(posts, function(i, data) {
              looper = looper.then(function() {
                  return $.ajax({
                      data: {
                          json: JSON.stringify(data),
                          delay: delay
                      },
                      method: 'post',
                      url: '/echo/json/',
                      dataType: 'json'
                  }).done(function(response) {
                      span.append('Response:<br />');
                      for(key in response) {
                          span.append(key + ': ' + response[key] + '<br />');
                      }
                      $('span').append('Waiting ' + delay + ' seconds<br /><br />');
                  });
              });
          });
      });
      

      【讨论】:

      • 我认为这就是我要找的...延期。
      【解决方案5】:

      您的问题是您一次调用所有 a s,但您想等待第一个周期,然后再进行下一个周期。您希望等待前一个“a”循环完成,然后再开始下一个循环。

      让我们假设 a,b,c 接受一个回调,并传递它,

      a 看起来像

      function a(param1, param2,callback) {
           $.post(..., function(result){
               if(result){
                  b(callback);
               } else {
                  console.log("failed a");
               }
           })
      }
      

      b 会这样:

      function b(callback) {
            $.post(..., function(result){
               if(result){
                  c(callback);
               } else {
                  console.log("failed b");
               }
           })
      }
      

      而 c 看起来像:

      function c(callback) {
           $.post(..., function(result){
               if(result){
                  console.log("successful");
               } else {
                  console.log("failed b");
               }
               callback();
           })
      }
      

      这让我们知道一个周期何时完成。这让我们可以写:

      var data = [{param1 : 1235, param2: 3214},  {param1 : 5432, param2: 9876}];
      
      var index = 0;
      (function updateData(){
          a(data[index].param1,data[index].param2,function(){ //call a with the data
              index++;//update the index 
              updateData(); // start the next cycle
          });
      });
      

      【讨论】:

      • 这是示例代码,用于学习。例如,您需要检查您是否完成了数组并且不进行递归
      • 我应该得到数组的长度吧?哈哈,对不起..我在考虑之前按添加评论
      • 检查index++后需要检查data.length。仅当索引在范围内时才调用 updateData
      猜你喜欢
      • 2016-12-01
      • 2014-09-15
      • 1970-01-01
      • 2015-07-24
      • 2014-03-15
      • 1970-01-01
      • 2013-11-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多