Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chain ajax and execute it in sequence. Jquery Deferred

I have 3 processes that needs ajax to complete. But it is asynchronous and it fails to do what I wanted to do..

Lets say:

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

I want it to execute like this

a
b
c

That code will work perfectly, as you can see.. but if use a loop.

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

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

It will not work as expected and will just do:

a
a
b
b
c
c

instead of

a
b
c
a
b
c
like image 640
Joey Hipolito Avatar asked May 05 '13 13:05

Joey Hipolito


2 Answers

There are many ways to write this kind of thing.

A flexible approach is separate "actions" from "sequence", allowing :

  • functions a, b, c to initiate an asynchronous (ajax) action, with no knowledge of how they are to be sequenced
  • a, b, c to be reusable, as part of one or more sequences or individually, as required.

Here's a way to code this approach, using .then() exclusively for the chaining logic :

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

Alternatively, if you want to have a single asynchronous function, a, called from within a loop then the code could be something like this :

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.
like image 94
Beetroot-Beetroot Avatar answered Oct 25 '22 05:10

Beetroot-Beetroot


You can chain asynchronous calls like ajax calls using jQuery's deferred object and using 'then'.

You can also change it to use functions that returns a deferred promise object, instead of an ajax call as I have in my example.

http://jsfiddle.net/q4cFv/

(Example with async function: 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 />');
            });
        });
    });
});
like image 36
thebreiflabb Avatar answered Oct 25 '22 05:10

thebreiflabb