0

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();
2
  • i suggest to not use $.Deferred but the promise pattern Commented Sep 8, 2016 at 11:45
  • Avoid the deferred antipattern and it gets a whole less code. Commented Sep 8, 2016 at 11:49

1 Answer 1

1

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;
    });
}
Sign up to request clarification or add additional context in comments.

4 Comments

Can i assume tertiary() should not have any arguments (you still have "callback") and its just a copy / paste artefact.
@Merlin No, tertiary needs a callback parameter, otherwise it doesn't know where to pass its results.
Apologies, I should have been more specific. I meant in the "promises" version of your answer. I understand it is needed in the callback version.
This went a long way to help me understand the many, many things I was doing wrong in structuring call backs. Thx!

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.