MerlinTheMagic
MerlinTheMagic

Reputation: 605

How to structure javascript callbacks in a chain of dependant functions?

I have only used simple callbacks, i.e. function that performs async AJAX call and calls back once done. Whenever things got anymore complicated I have used $.Deferred(). The problem is that handling promises is a lot of code every time, i would like to know how to use callbacks correctly instead.

Here is my example. (the problem arises in the return from secondary()):

function primary()
{
    //call secondary with self as callback
    var data    = secondary("someStr", primary);
    if (data !== false) {
        //logic that uses data
    }
}

function secondary(str, callback)
{
    //call the third function. Since we need parameters
    //on the callback, we create anon function
    var returnFromThird = tertiary(function() {
        secondary(str, callback);
    });

    if (returnFromThird !== false) {
        //when we have data from third do someting....
        //but here lies the problem, how do i callback primary()?
        return returnFromThird + " " + str;
    } else {
        //third is not yet ready
        return false;
    }
}

var myVar3  = null;
function tertiary(callback)
{
    if (myVar3 === null) {
        //do async ajax call that sets myVar3 value
        var ajaxRequest = $.ajax({
            url: "/someUrl",
            type: "POST",
            data: {myData : "blabla"},
            async: true,
        });
        ajaxRequest.done(function(data) {
            myVar3  = data;
            //issue the call back, so secondary() can come get myVar3
            callback();
        });
        //we did not have the required data so we return false
        return false;
    } else {
        return myVar3;
    }
} 

//execute
primary();

Here is how i would normally handle the issue using JQuery Deferred:

function primary()
{
    var promise = secondary(str);
    $.when(promise).then(
        function (data) {
            //logic that uses data
        }
    );
}

function secondary(str)
{
    var dfd     = $.Deferred();
    var promise = tertiary();
    $.when(promise).then(
        function (data) {
            dfd.resolve(data + " " + str);
        }
    );
    return dfd.promise();
}

var myVar3  = null;
function tertiary()
{
    var dfd     = $.Deferred();
    if (myVar3 === null) {
        var ajaxRequest = $.ajax({
            url: "/someUrl",
            type: "POST",
            data: {myData : "blabla"},
            async: true,
        });
        ajaxRequest.done(function(data) {
            myVar3  = data;
            dfd.resolve(myVar3);
        });

    } else {
        dfd.resolve(myVar3);
    }
    return dfd.promise();
}

primary();

Upvotes: 0

Views: 136

Answers (1)

Bergi
Bergi

Reputation: 664444

If you are using callbacks, you should always call the callback, not sometimes return a value:

var myVar3 = null;
function tertiary(callback) {
    if (myVar3 === null) {
        //do async ajax call that sets myVar3 value
        $.post("/someUrl", {myData : "blabla"}).done(function(data) {
            myVar3  = data;
            callback(data); // just pass the data to the callback
        });
    } else {
        return callback(myVar3); // always call the callback with the data
    }
}

Now your other functions would look like this:

function primary() {
    secondary("someStr", function(data) {
        //logic that uses data
    });
}

function secondary(str, callback) {
    tertiary(function(returnFromThird) {
        callback(returnFromThird + " " + str);
    })
}

But you are right, you should be using promises, it simplifies this a great lot:

var myVarPromise = null;
function tertiary() {
    if (myVarPromise === null)
        myVarPromise = $.post("/someUrl", {myData : "blabla"});
    return myVarPromise;
}
function primary() {
    return secondary("someStr").then(function(data) {
        //logic that uses data
    });
}
function secondary(str) {
    return tertiary().then(function(returnFromThird) {
        return returnFromThird + " " + str;
    });
}

Upvotes: 1

Related Questions