【问题标题】:How to chain ajax requests?如何链接ajax请求?
【发布时间】:2009-06-15 11:05:44
【问题描述】:

我必须与强制我链接请求的远程 api 交互。那是异步模式下的回调地狱:

// pseudocode: ajax(request_object, callback)
ajax(a, function() {
  ajax(b(a.somedata), function() {
    ajax(c(b.somedata), function() {
      c.finish()
    }
  }) 
})

在同步模式下会更易读:

sjax(a)
sjax(b(a.somedata))
sjax(c(b.somedata))
c.finish()

但是 Sjax 是邪恶的 :) 我如何以一种 nice 不那么邪恶且可读的方式做到这一点?

【问题讨论】:

    标签: javascript jquery asynchronous


    【解决方案1】:

    你可以有一个函数,它传递一个整数来说明请求所在的步骤,然后使用 switch 语句来确定下一步需要发出什么请求:

    function ajaxQueue(step) {
      switch(step) {
        case 0: $.ajax({
                  type: "GET",
                  url: "/some/service",
                  complete: function() { ajaxQueue(1); } 
        }); break;
        case 1: $.ajax({
                  type: "GET",
                  url: "/some/service",
                  complete: function() { ajaxQueue(2); }
                }); break;
        case 2: $.ajax({
                  type: "GET",
                  url: "/some/service",
                  complete: function() { alert('Done!'); }
                }); break;
      }
    }
    
    ajaxQueue(0);
    

    希望有帮助!

    【讨论】:

    • 你不能以这种方式给参数默认值。但我喜欢这个解决方案。
    • 哦该死的对不起,最近编写了很多 PHP 并且我的 JavaScript 生锈了。以正确的语法发布更新:)
    • ASP.NET 3.5 的 WebServices 是这样设置的。 JS 端的所有 WebMethod 都有一个默认的回调函数(好吧,如果你这样设置的话),你可以打开服务器上调用的方法名称来决定如何处理响应。在这个例子中我可能会改变的一件事是不传递数字,而是使用更多类似枚举的方法:ajaxQueue(some_name);所以你知道你的函数调用在做什么。
    【解决方案2】:

    不要使用匿名函数。给他们起名字。我不知道你是否能够做到我在下面写的:

    var step_3 = function() {
        c.finish();
    };
    
    var step_2 = function(c, b) {
        ajax(c(b.somedata), step_3);
    };
    
    var step_1 = function(b, a) {
      ajax(b(a.somedata), step_2);
    };
    
    ajax(a, step_1);
    

    【讨论】:

      【解决方案3】:

      如果回调总是返回下一个请求所需的参数,则此函数应将 ajax 请求列表链接在一起:

      function chainajax(params, callbacks) {
        var cb = shift(callbacks);
        params.complete = function() {
          var newparams = cb(arguments);
          if (callbacks)
            chainajax(newparams, callbacks);
        };
        $.ajax(params);
      };
      

      您可以分别定义这些回调函数,然后将它们链接在一起:

      function a(data) {
        ...
        return {type: "GET", url: "/step2.php?foo"}
      };
      // ...
      function d(data) { alert("done!"); };
      
      chainajax({type: "GET", url: "/step1.php"},
        [a, b, c, d]);
      

      您还可以在对chainajax 的调用中声明“内联”函数,但这可能会有点混乱。

      【讨论】:

        【解决方案4】:

        也许您可以做的是编写一个服务器端包装函数。这样,您的 javascript 只会对您自己的 Web 服务器进行一次异步调用。然后你的 web 服务器使用 curl(或 urllib 等)与远程 API 交互。

        【讨论】:

        • @PatrikAkerstrand,服务器上的 urllib - 比客户端上的嵌套回调更难阅读。 =)
        【解决方案5】:

        更新:如果您使用 jQuery,我已经了解了一个更好的答案,请参阅标题下的更新:使用 jQuery Defered

        旧答案:

        您还可以使用Array.reduceRight(如果可用)来包装$.ajax 调用并将类似的列表:[resource1, resource2] 转换为$.ajax({url:resource1,success: function(...) { $ajax({url: resource2...(我从 Haskell 中学到的一个技巧,它是 fold/foldRight函数)。

        这是一个例子:

        var withResources = function(resources, callback) {
            var responses = [];
            var chainedAjaxCalls = resources.reduceRight(function(previousValue, currentValue, index, array) {
                return function() {
                    $.ajax({url: currentValue, success: function(data) {
                        responses.push(data);
                        previousValue();
                    }})
                }
            }, function() { callback.apply(null, responses); });
            chainedAjaxCalls();
        };
        

        那么你可以使用:

        withResources(['/api/resource1', '/api/resource2'], function(response1, response2) {
            // called only if the ajax call is successful with resource1 and resource2
        });
        

        使用 jQuery 延迟

        如果您使用 jQuery,则可以通过使用 jQuery.when() 函数来利用 jQuery Deffered

         jQuery.when($.get('/api/one'), $.get('/api/two'))
               .done(function(result1, result2) { 
                      /* one and two is done */
                });
        

        【讨论】:

        【解决方案6】:

        Check out this FAQ item on the jQuery site. 特别是回调引用和完整方法。

        你想要的是 A 的数据传递给 B,B 的数据传递给 C。所以你会在完成时做一个回调。

        我还没试过。

        【讨论】:

          【解决方案7】:

          我相信实现状态机会让代码更具可读性:

          var state = -1;
          var error = false;
          
          $.ajax({success: function() { 
                            state = 0;
                            stateMachine(); },
                  error: function() {
                            error = true;
                            stateMachine();
                  }});
          
          function stateMachine() {
            if (error) {
               // Error handling
               return;
            }
          
            if (state == 0) {
              state = 1;
              // Call stateMachine again in an ajax callback
            }
            else if (state == 1) {
          
            }
          }
          

          【讨论】:

          • 如果可能,应该避免使用全局变量。 Jamie Rumbelow 的解决方案更加优雅。
          【解决方案8】:

          我用 Promises 做了一个方法

          // How to setup a chainable queue method
          var sequence = Promise.resolve();
          
          function chain(next){
              var promise = new Promise(function(resolve){
                  sequence.then(function(){
                      next(resolve);
                  });	
              });
          
              sequence = promise;
          }
          
          // How to use it
          chain(function(next){
              document.write("<p>start getting config.json</p>");
              setTimeout(function(){
              	document.write("<p>Done fetching config.json</p>");
                  next();
              }, 3000);
          });
          
          chain(function(next){
              document.write("<p>start getting init.js</p>")
              setTimeout(function(){
                  document.write("<p>starting eval scripting</p>");
                  next();
              }, 3000);
          });
          
          chain(function(next){
              document.write("<p>Everything is done</p>");
          });

          奖励:一个超轻的 138 字节限制的 A-Promise(只能解析 - 没有参数,并且只能调用最后一个 then-method)

          背景: 我在没有承诺 ATM 的地方为 node.js 制作了这个。我不想要一个完全成熟的 Promise 库,我依赖它并且必须包含在我的 package.json 中,我需要它快速、轻便并且只做一件事。我只需要它做一件事(像你想要的那样链接东西)

          function Q(a,b){b=this;a(function(){b.then&&b.then();b.then=i});return b}function i(a){a&&a()}Q.prototype={then:function(a){this.then=a}};
          

          如何?

          // Start with a resolved object
          var promise = new Q(function(a){a()});
          // equal to 
          // var promise = Promise.resolve();
          
          // example usage
          new Q(function(resolve){
              // do some async stuff that takes time
              // setTimeout(resolve, 3000);
          }).then(function(){
              // its done
              // can not return a new Promise
          }); // <- can not add more then's (it only register the last one)
          

          对于链式队列方法

          // How to setup a chainable queue method with ultraligth promise
          var sequence = new Q(function(a){a()});
          
          function chain(next){
              var promise = new Q(function(resolve){
                  sequence.then(function(){
                      next(resolve);
                  }); 
              });
          
              sequence = promise;
          }
          

          【讨论】:

            【解决方案9】:

            完整的回调就是你要找的:

            $.ajax({
                 type: 'post',
                 url: "www.example.com",
                 data: {/* Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests. */},
                 success:
                      function(data) {
                          /* you can also chain requests here. will be fired if initial request is successful but will be fired before completion. */
                      },
                complete: 
                     function() {
                         /* For more a more synchronous approach use this callback. Will be fired when first function is completed. */
                     }
            });
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-06-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多