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();
MerlinTheMagic
  • 575
  • 5
  • 16
  • i suggest to not use `$.Deferred` but the [promise pattern](https://www.promisejs.org/) –  Sep 08 '16 at 11:45
  • [Avoid the deferred antipattern](http://stackoverflow.com/q/23803743/1048572) and it gets a whole less code. – Bergi Sep 08 '16 at 11:49

1 Answers1

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;
    });
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Can i assume tertiary() should not have any arguments (you still have "callback") and its just a copy / paste artefact. – MerlinTheMagic Sep 08 '16 at 13:39
  • @Merlin No, `tertiary` *needs* a `callback` parameter, otherwise it doesn't know where to pass its results. – Bergi Sep 08 '16 at 13:43
  • 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. – MerlinTheMagic Sep 08 '16 at 13:47
  • This went a long way to help me understand the many, many things I was doing wrong in structuring call backs. Thx! – MerlinTheMagic Sep 08 '16 at 19:51