关于javascript:如何使用jQuery promises链接三个异步调用?

How do I chain three asynchronous calls using jQuery promises?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function first()
{
   ajax()
}

function second()
{
   ajax()
}

function third()
{
   ajax()
}


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

我尝试对这两个函数使用deferred,并提出了一个局部解。我可以把它扩展到三个功能吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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)})


在每种情况下,返回$.ajax()返回的jqxhr对象。

这些对象承诺兼容,因此可以与.then()/.done()/.fail()/.always()链接。

在这种情况下,你想要的就是.then(),和问题中的完全一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
}

参数datatextStatusjqXHR是由前一个函数的$.ajax()调用产生的,即first()second()提供数据,second()third()提供数据。

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


在jquery中使用承诺实际上有一种更简单的方法。请看以下内容:

1
2
3
4
5
6
7
8
9
10
11
$.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/


回答很晚,但我想答案缺少一些直接链接的代码。通过jquery中的Promise支持,链接事件非常简单。我使用以下链接:

1
2
3
4
5
6
7
8
9
10
$.ajax()
.then(function(){
   return $.ajax() //second ajax call
})
.then(function(){
   return $.ajax() //third ajax call
})
.done(function(resp){
   //handle final response here
 })

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


比那简单多了。

$.ajax已经返回了一个承诺(延迟对象),所以您可以简单地写

1
2
3
function first() {
    return $.ajax(...);
}


你可以用更实用的方式来写:

1
2
3
4
5
6
7
8
[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)

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

这里是我自己实现的类似方法,有点难看,但可能有效。它将每个方法的结果作为一个?进度更新?&在返回的承诺对象上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  $.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();
  };

如何在你的情况下使用它?也许不太漂亮,但它应该起作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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变量。

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

1
2
3
4
5
6
7
8
9
function first() {
    return ajax(...);
}
function second(first_ret) {
    return ajax(first_ret.id, ...);
}
function third(second_ret) {
    return ajax(second_ret.something, ...);
}


实现这一点的最佳方法是为此创建一个可重用的函数。这甚至可以通过使用reduce的一行代码来实现:

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

此函数接受返回Promise对象的回调数组,如三个函数。

示例用法:

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

这样,first的结果也将自动成为second的参数,所以基本上发生的是:

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

当然,您可以向数组中添加任意多的函数。


以下功能似乎可以工作,并允许函数列表是动态的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<html>
  <head>
  demo chained synchronous calls
  </head>
  <body>

  <script src="http://code.jquery.com/jquery-2.2.4.min.js">
  <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();
    });
 

  </body>
</html>


要链接jquery ajax调用,我做了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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();
     })
   });

我也遇到了同样的问题,链接Ajax调用。经过几天的努力,我终于做到了。完全按照我的意愿去做的。我只是没想到。它可能会帮助其他人…