85

I have three HTTP calls that need I need to make in a synchronous manner and how do I pass data from one call to the other?

function first()
{
   ajax()
}

function second()
{
   ajax()
}

function third()
{
   ajax()
}


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

I tried to use the deferred for the two functions and I came up with a partial solution. Can I extend it to be for three functions?

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)})
4
  • Looks like pretty much an exact duplicate of How to chain ajax calls using jquery Commented Apr 16, 2013 at 0:46
  • 1
    sniker jQuery.Promise().Promise().Promise(void) Commented Apr 16, 2013 at 0:48
  • 3
    The solution in the "duplicate" question is I think old and obselete. Commented Apr 16, 2013 at 1:19
  • 1
    Yeah, but the suggestion to use q from the update is a good one: github.com/kriskowal/q. For more complicated stuff, seq is worth a look too: github.com/substack/node-seq Commented Nov 5, 2013 at 13:53

9 Answers 9

99

In each case, return the jqXHR object returned by $.ajax().

These objects are Promise-compatible so can be chained with .then()/.done()/.fail()/.always().

.then() is the one you want in this case, exactly as in the question.

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);
}

Arguments data, textStatus and jqXHR arise from the $.ajax() call in the previous function, ie. first() feeds second() and second() feeds third().

DEMO (with $.when('foo') to deliver a fulfilled promise, in place of $.ajax(...)).

Sign up to request clarification or add additional context in comments.

10 Comments

This doesn't work for me. When I try you example the results from first are passed to second AND third. So first feeds second and third. Not first feeds second, second feeds third. $.ajax({...}).then(function(..){ return $.ajax({...}); }).then( function(...){ /* I want the results from the second call. */ });
No time right now to run a test but I can't see why the code in my answer shouldn't work. Make sure you are using jQuery 1.8.3 or above. Earlier versions may well give the symptom you describe.
just checked with jquery 1.11.0 and all then calls get arguments from when and not from previous then.
@pajics, I have added a demo to the answer. Either 1.11.0 has reverted or you are doing something wrong. Can you provide a fiddle?
Unfortunately I cant recreate my code with fiddle (using gmaps) but when I tried something simple like this: jsfiddle.net/CbPbw/1 it worked as expected. I'll have to find out what's wrong with my code. Thanks
|
47

There is actually a much easier approach when using promises with jQuery. Have a look at the following:

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

Simply chain all your calls into the $.when(...) call and handle the return values in the .done(...) call.

Here's a walkthrough if you prefer: http://collaboradev.com/2014/01/27/understanding-javascript-promises-in-jquery/

7 Comments

@Emilio, yes precisely. That is what the OP asked for and this answer does not deliver in that regard.
Not the answer the OP was looking for, but the one I was. Thanks!
Answers a completely different question. Recommend removing in order to avoid confusion.
The three ajax calls in this example will happen asynchronously
This answer doesn't chain the requests, but are rather run in parallel.
|
38

Quite late to reply, but I guess answers are missing some straight forward code for chaining. Chaining events is pretty simple with promise support in jquery. I use the following for chaining:

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

It's simple with no complicated for loops or multiple nested callbacks.

1 Comment

Which arguments will be passed to the anonymous functions in your example? The response from the server?
17

It's much simpler than that.

$.ajax already returns a promise (Deferred object), so you can simply write

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

4 Comments

@SLaks Is it possible to have more than one deferred in the then? As in first().then(second, third).then(fourth); ?
How do I chain multiple ajax calls and pass the return value? deferred.resolve used to be the way I used to do it.
@JohnMcdock: Return a value from .then() (in 1.8+). See api.jquery.com/deferred.then
@SLaks - please extend your answer to better explain what is asked, since it seems your answer is a very important one to the nature and basics of jQuery ajax object, it deserves a few more lines which explains how to chain multiple async functions
9

The best way to do this is by making a reusable function for this. This can even be done with just one line of code using reduce:

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

This function accepts an array of callbacks which return a promise object, like your three functions.

Example usage:

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

This way the result of first will also automatically be the parameter of second, so basically what happens is this:

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

And of course you could add as many functions to the array as you want.

1 Comment

By far the best solution. I picked up the snippet and integrated it into a library requiring groupings of chained Ajax events. Sadly, I had to change the ES6 syntax, but still a clean solution. Well done. I hope more people vote this one up.
6

You can write it in more functional manner:

[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)

Comments

4

I found a good looking solution here: How do I chain a sequence of deferred functions in jQuery 1.8.x?

And here is my own implementation of similar approach, somewhat ugly but probably working. It broadcasts result of each method as a «progress update» on returned promise object.

  $.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();
  };

How to use it for your situation? Maybe not beautiful, but it should work:

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');
});

Or you can just assign that id variable from first function.

Or if you only need previous function's result, you can use this approach:

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

3 Comments

When you call a notify() method on a Deferred object, you pass an object to it. Then anyone who subscribed to progress updates of that Deferred's promise will be called with that object.
It worked for me ! I only needed to change function next() to function next(ret), becaouse ret was not defined :)
Thanks @ilian6806, fixed.
0

The following appears to work and allows the list of functions to be dynamic:

<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>

Comments

-2

To chain jquery ajax calls i did :

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();
     })
   });

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.