如何使用jQuery Promise链接三个异步调用?


69

我有三个需要以同步方式进行的HTTP调用,如何将数据从一个调用传递到另一个调用?

function first()
{
   ajax()
}

function second()
{
   ajax()
}

function third()
{
   ajax()
}


function main()
{
    first().then(second).then(third)
}

我尝试将deferred用于这两个函数,并提出了部分解决方案。我可以扩展为三个功能吗?

function first() {
    var deferred = $.Deferred();
     $.ajax({

             "success": function (resp)
             {

                 deferred.resolve(resp);
             },

         });
    return deferred.promise();
}

function second(foo) {
     $.ajax({
            "success": function (resp)
            {
            },
            "error": function (resp)
            {
            }
        });
}


first().then(function(foo){second(foo)})


1
snikerjQuery.Promise()。Promise()。Promise(void)
Jay

1
我认为“重复”问题中的解决方案已过时且过时。
约翰·麦考德

1
是的,但是从更新中使用q的建议是一个很好的建议:github.com/kriskowal/q。对于更复杂的东西,seq也值得一看:github.com/substack/node-seq
Milimetric

Answers:


84

在每种情况下,都返回由返回的jqXHR对象$.ajax()

这些对象是无极兼容,以便在与被链接.then()/ .done()/ .fail()/ .always()

.then() 在这种情况下正是您想要的那个,正好在问题中。

function first() {
   return $.ajax(...);
}

function second(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function third(data, textStatus, jqXHR) {
   return $.ajax(...);
}

function main() {
    first().then(second).then(third);
}

参数datatextStatus并且jqXHR来自$.ajax()上一个函数(即)中的调用。first()饲料second()second()饲料third()

DEMO$.when('foo')用于代替履行承诺$.ajax(...))。


7
这对我不起作用。当我尝试您的示例时,结果从第一个传递到第二个和第三个。所以第一喂第二和第三。不是第一个进给第二个,第二个进给第三个。$ .ajax({...})。then(function(..){return $ .ajax({...});})。then(function(...){/ *我希望得到结果第二个电话* /});
Jerinaw 2014年

1
现在没有时间运行测试,但是我看不到答案中的代码为什么不起作用。确保您使用的是jQuery 1.8.3或更高版本。早期版本可能会给您您描述的症状。
甜菜根

2
刚刚使用jquery 1.11.0进行了检查,然后所有调用都从when而不是从then那里获取参数。
pajics,2014年

1
@pajics,我在答案中添加了一个演示。1.11.0还原了,或者您做错了什么。你能提供一个小提琴吗?
甜菜根-甜菜根

1
不幸的是,我无法用小提琴(使用gmaps)重新创建代码,但是当我尝试像这样的简单操作:jsfiddle.net/CbPbw/1时,它按预期工作。我将不得不找出我的代码出了什么问题。谢谢
pajics

39

在jQuery中使用promise时,实际上有一种更容易的方法。看看以下内容:

$.when(
    $.ajax("/first/call"),
    $.ajax("/second/call"),
    $.ajax("/third/call")
    )
    .done(function(first_call, second_call, third_call){
        //do something
    })
    .fail(function(){
        //handle errors
    });

只需将所有调用链接到$ .when(...)调用中,并处理.done(...)调用中的返回值。

如果您愿意,可以通过以下演练:http : //collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/


9
@Emilio,是的。这就是OP所要求的,而在这方面,这个答案没有实现。
甜菜根-甜菜根

16
不是OP想要的答案,而是我想要的答案。谢谢!
丹尼尔·巴克马斯特2014年

9
回答一个完全不同的问题。建议删除以免造成混淆。
德里克·亨德森

16
在此示例中,三个ajax调用将异步
jchook

7
这个答案不会链接请求,而是并行运行。
阿图罗·托雷斯·桑切斯

30

答复很晚,但是我想答案缺少一些用于链接的简单代码。借助jquery中的promise支持,链接事件非常简单。我将以下内容用于链接:

$.ajax()
.then(function(){
   return $.ajax() //second ajax call
})
.then(function(){
   return $.ajax() //third ajax call
})
.done(function(resp){
   //handle final response here
 })

它很简单,没有复杂的循环或多个嵌套的回调。


14

比这简单得多。

$.ajax 已经返回了一个Promise(延迟对象),因此您可以简单地编写

function first() {
    return $.ajax(...);
}

@SLaks届时是否可以推迟多个?如first().then(second, third).then(fourth);
Mark Pieszak-Trilon.io

2
如何链接多个ajax调用并传递返回值?deferred.resolve曾经是我以前使用的方法。
约翰·麦克多克

@JohnMcdock:从.then()(1.8及更高版本)返回一个值。请参阅api.jquery.com/deferred.then
SLaks

5
@SLaks-请扩展您的答案以更好地解释所要询问的内容,因为您的答案似乎对jQuery ajax对象的性质和基础非常重要,它还需要多几行内容来说明如何链接多个异步函数
vsync

6

您可以通过更实用的方式编写它:

[function() { return ajax(...)}, function(data) { return ajax(...)}]
.reduce(function(chain, callback) { 
  if(chain) { 
    return chain.then(function(data) { return callback(data); });
  } else {
    return callback();
  }
}, null)

5

最好的方法是为此创建一个可重用的函数。甚至只需使用reduce以下一行代码即可完成:

function chainPromises(list) {
    return list.reduce((chain, func) => chain ? chain.then(func) : func(), null);
}

此函数接受一个数组数组,这些数组将返回一个promise对象,就像您的三个函数一样。

用法示例:

chainPromises([first, second, third]).then(function (result) {
    console.log('All done! ', result);
});

这样,的结果也first将自动成为的参数second,因此基本上会发生以下情况:

first().then(function(res1) { return second(res1) })
       .then(function(res2) { return third(res2)  })
       .then(function(result) { console.log('All done! ', result) });

当然,您可以根据需要向数组添加尽可能多的函数。


1
迄今为止最好的解决方案。我拿起了代码片段,并将其集成到需要对链式Ajax事件进行分组的库中。可悲的是,我不得不更改ES6语法,但是仍然是一个干净的解决方案。做得好。我希望更多的人投票赞成这一点。
尼克·约翰逊

4

我在这里找到了一个不错的解决方案:如何在jQuery 1.8.x中链接一系列延迟函数?

这是我自己的类似方法的实现,虽然有点丑陋,但可能有效。它在返回的promise对象上以“进度更新”的形式广播每种方法的结果。

  $.chain = function() {
      var defer = $.Deferred();
      var funcs = arguments;
      var left = funcs.length;
      function next(lastResult) {
          if(left == 0) {
              defer.resolve();
              return;
          }
          var func = funcs[funcs.length - left]; // current func
          var prom = func(lastResult).promise(); // for promise will return itself,
                                       // for jquery ojbect will return promise.
          // these handlers will be launched in order we specify them
          prom.always(function() {
              left--;
          }).done(function(ret) {
              defer.notify({
                  idx: funcs.length-left,
                  left: left,
                  result: ret,
                  success: true,
              });
          }).fail(function(ret) {
              defer.notify({
                  idx: funcs.length-left,
                  left: left,
                  result: ret,
                  success: false,
              });
          }).always(function(ret) {
              next(ret);
          });
      }
      next();
      return defer.promise();
  };

如何根据您的情况使用它?也许不漂亮,但是应该可以工作:

function first() {
    return ajax(...);
}

var id;

funciton second() {
    return ajax(id, ...);
}

function third() {
    return ajax(id, ...);
}

$.chain(first, second, third).progress(function(p) {
    if(p.func == first)
        id = p.result.identifier;
}).then(function() {
    alert('everything is done');
});

或者,您可以仅通过first函数分配该id变量。

或者,如果仅需要上一个函数的结果,则可以使用以下方法:

function first() {
    return ajax(...);
}
function second(first_ret) {
    return ajax(first_ret.id, ...);
}
function third(second_ret) {
    return ajax(second_ret.something, ...);
}

在对象notify()上调用方法时Deferred,会将对象传递给它。然后人谁订阅了该进度的更新Deferredpromise会与该对象进行调用。
MarSoft's

它对我有用!我只需要将函数next()更改为函数next(ret),因为ret尚未定义:)
ilian6806

感谢@ ilian6806,已修复。
MarSoft

1

以下内容似乎起作用,并且允许功能列表是动态的:

<html>
  <head>
  <title>demo chained synchronous calls</title>
  </head>
  <body>

  <script src="http://code.jquery.com/jquery-2.2.4.min.js"></script>
  <script type="text/javascript">
    function one(parms) {
        console.log('func one ' + parms);
        return 1;
    }

    function two(parms) {
        console.log('func two ' + parms);
        return 2;
    }

    function three(parms) {
        console.log('func three ' + parms);
        return 3;
    }

    function four(parms) {
        console.log('func four ' + parms);
        return 4;
    }

    var funcs = ['one', 'two', 'three', 'four'];
    var rvals = [0];

    function call_next_func() {
        if (funcs.length == 0) {
            console.log('done');
        } else {
            var funcname = funcs.shift();
            console.log(funcname);
            rvals.push(window[funcname](rvals));
            call_next_func();
        }
    }

    $(document).ready(function($){
        call_next_func();
    });
  </script>

  </body>
</html>


-1

要链接jquery ajax调用我做了:

function A(){
     return $.ajax({
      url: url,
      type: type,
      data: data,
      datatype: datatype,
      success: function(data)
      {
        code here
      }
    });
   }

   function B(){
     return $.ajax({
      url: url,
      type: type,
      data: data,
      datatype: datatype,
      success: function(data)
      {
        code here
      }
    });
   }

   function C(){
     return $.ajax({
      url: url,
      type: type,
      data: data,
      datatype: datatype,
      success: function(data)
      {
        code here
      }
    });
   }

   A().done(function(data){
     B().done(function(data){
        C();
     })
   });

-4

我只是遇到了同样的问题,将ajax调用链接在一起。经过几天的尝试,我终于$.ajax({ async: false,... 完成了我想存档的全部工作。我只是没有想到这一点。这可能会帮助其他人...


1
通常是个坏主意。您的线程将被阻止,因此在请求完成之前您无法执行其他任何操作。这通常会导致糟糕的用户体验
查理·马丁

你是绝对正确的。但这是我想要的行为。我扩展了一组不可预测的元素,它们像一棵树,取决于彼此的存在。
MelP

2
这很容易异步完成。当您现在同步执行此操作时,Chrome浏览器甚至会发出警告... Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.
Charlie Martin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.