2

I need to run a series of calls over websockets via Socket.IO (client-side). Since I'm not using $.ajax, jQuery's deferred functions won't integrate as well and I'll have to manually handle promises. With every websocket call, I pass a callback and I'm quickly seeing how this project could spiral out of control. Here's a simplified example of how my websocket calls work (excluding all connection handling code):

function js2node(nodeFunction, data, callback){
    socket.emit('incoming', nodeFunction, data, callback);
}

function sampleServerCall(){
    js2node('sampleCall', 'something', 'sampleCallback');
}

function sampleCallback(json){
    // Handle data
}

sampleServerCall();

I will be talking to the server quite a bit, all calls will be asynchronous, but some will need to come back in a specific order. Enter jQuery deferred. Here is some working code:

var deferredArray = [];

$(function(){
    $.when(  // Any order
        getData1(),
        getData2()
    ).then(function(){  // Must have responses from dataCallback1 and dataCallback2 before doing this...
        $.when(  // Any order
            getData3(),
            getData4()
        ).then(function(){  // Must have responses from dataCallback3 and dataCallback4 before doing this...
            getData5();
        });
    });
});

function getData1(){
    js2node('data1', 'something', 'dataCallback1');
    deferredArray[0] = new $.Deferred();
    return deferredArray[0].promise();
}

function getData2(){
    js2node('data2', 'something', 'dataCallback2');
    deferredArray[1] = new $.Deferred();
    return deferredArray[1].promise();
}

function getData3(){
    js2node('data3', 'something', 'dataCallback3');
    deferredArray[2] = new $.Deferred();
    return deferredArray[2].promise();
}

function getData4(){
    js2node('data4', 'something', 'dataCallback4');
    deferredArray[3] = new $.Deferred();
    return deferredArray[3].promise();
}

function getData5(){
    js2node('data5', 'something', 'dataCallback5');
    deferredArray[4] = new $.Deferred();
    return deferredArray[4].promise();
}

function dataCallback1(json){
    // Handle data
    deferredArray[0].resolve();
}

function dataCallback2(json){
    // Handle data
    deferredArray[1].resolve();
}

function dataCallback3(json){
    // Handle data
    deferredArray[2].resolve();
}

function dataCallback4(json){
    // Handle data
    deferredArray[3].resolve();
}

function dataCallback5(json){
    // Handle data
    deferredArray[4].resolve();
}

As you can see, I'm still stuck with nested callbacks from the way I'm using when/then and nesting could potentially go deeper as I add functionality. Deferred is a new concept to me but I've read it's supposed to help in situations such as this. I feel like there has to be a better way than what I'm currently doing. Can anyone help me set this up more efficiently?

2
  • 1
    Take a look at stackoverflow.com/a/20326123/461055, the answer gives an example of how to execute deferred objects in order. Commented Dec 4, 2013 at 20:10
  • @Sumit, thanks, I'll have to play around with that some. I think I got what I need from the answers below but this should help too! Commented Dec 5, 2013 at 13:39

2 Answers 2

6

You can do more with .then:

$(function(){
    $.when(
        doSock('data1', 'something'),
        doSock('data2', 'something')
    ).then(function(data1, data2){
        return $.when(
            doSock('data3', 'something'),
            doSock('data4', 'something')
        );
    }).then(function(data3, data4){
        return doSock('data5', 'something');
    });
});

That way your nesting never goes deeper than that.

(i used adeneo's helper method)

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

7 Comments

+1, indeed, returning the promise from the $.when method is one way to go.
Great, this resolves the nesting issue and should help the team better follow my intentions!
Unfortunately, this is not working as expected. The "doSock" call within the 2nd .then (data5) is executing before the resolve is actually given from the functions in the 2nd $.when (data3 and data4). As a matter of fact, the variables received in the 2nd .then function (data3, data4) are actually the same data from the first .then function (data1, data2)! Sorry, I'm sure this is confusing to read.
It seems to be working for me, when i substitute the socket request with a setTimeout: jsfiddle.net/PpZkd
Ah, simply replace .then with .pipe. don't forget to go back to .then when you upgrade to 1.8 jsfiddle.net/PpZkd/2
|
3

Using a better helper function sure would help, but you'd still have to structure the calls with $.when and $.then to execute them in the proper order

function doSock(nodeFunction, data) {
    var def = new $.Deferred();
    socket.emit('incoming', nodeFunction, data, function(received) {
        def.resolve(received)
    });
    return def.promise();
}

$(function(){
    $.when(
        doSock('data1', 'something'),
        doSock('data2', 'something')
    ).then(function(data1, data2){
        $.when(
            doSock('data3', 'something'),
            doSock('data4', 'something')
        ).then(function(data3, data4){
            doSock('data5', 'something');
        });
    });
});

3 Comments

Thanks, that helper function will work nicely to clean up a lot of the redundancy mess! I will definitely be using a combo of your answer and Kevin's answer. Despite cleaning up my code better, I think I have to accept Kevin's for countering the nesting situation. Grr, wish I could accept both! I really appreciate your help.
After implementing this, I do have a question. In the line with the first instance of "then", is there any sort of guarantee variable "data1" will come from the doSock call "data1"? In other words, will whatever variables I define within the then function ALWAYS be in the same order I define list the functions (not necessarily when they finish)? This seems to be the case but I just want to make sure it's safe to rely on that assumption.
Yes, the order of the data coming back as arguments in $.then is always the same as the order of doSock functions etc. It's done this way so you know what result comes where, otherwise it would be hard to manage.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.