Skip to content Skip to sidebar Skip to footer

Calling A Function After Another Function's Callbacks Are Returned

This is the simplified version of my problem: var callback_one = function (result_from_web_service) { console.log('callback one'); }; var callback_two = function (result_from_we

Solution 1:

The only way to make something like this work properly is to make y asynchronous. Basically, y internally waits for x to complete before executing its own code. This is similar to things like domready or onload which waits for other things to happen before executing their own logic.

There are two ways to accomplish this. The first, simplest and most naive way is setTimeout polling. Make x set a variable or attribute and check that before executing:

function y () {
    if (x.ready) {
        /* do what you need to do here */
    }
    else {
        setTimeout(y,100);
    }
}

The second method is to create virtual events or promises. Not all promise libraries support using a promise that has already expired (my own homemade one does) so you may need to write your own control flow to handle that case. For this you need to rewrite x to support an event or promise-like api:

var x = (function () {
    var async_calls = 2;
    var callback;

    f = function () {
        console.log('x is called');
        async_call_one(function(){
            async_calls --;
            if (async_calls == 0 && callback) callback();
            callback_one();
        }); 
        async_call_two(function(){
            async_calls --;
            if (async_calls == 0 && callback) callback();
            callback_two();
        });
    }

    f.loaded = function (loaded_callback) {
        callback = loaded_callback;
        if (async_calls == 0) callback();
    }         

    return f;
})();

Now in y you can use the x.loaded function to execute your code when x is loaded:

functiony() {
    x.loaded(function(){
        /* do what you need to do here */
    });
}

Of course, this has the problem of making y asynchronous. Therefore if your users expect to write something like:

y();
a();
b();

then a and b may or may not execute before y. To solve this you need to make y accept a callback so that you users can control their program flow:

function y (callback) {
    if (x.ready) {
        /* do what you need to do here */callback();
    }
    else {
        setTimeout(function(){y(callback)},100);
    }
}

or:

functiony(callback) {
    x.loaded(function(){
        /* do what you need to do here */
        callback();
    });
}

So they'd have to use y like this:

y(function(){
    a();
    b();
});

Alternatively you can make y return a promise if you prefer that style.

Solution 2:

A little bit down the road to spaghetti code but you could wrap up the two callbacks together by setting variables they both can see and only call y() when they both return.

for example

var finished = false;
var aync_call_one = function () {
    //stuffif (finished) {
       y();
    } else {
       finished = true;
    }
}

var aync_call_two = function () {
    //stuffif (finished) {
       y();
    } else {
       finished = true;
    }
}

However if you have to use jquery promises, you need to return a promise object to use $.when,

var deferred = $.Deferred();
return deferred.promise();

and inside the asyncs you need to do

deferred.resolve();

though that would only work for one of your async calls so you would need another $.when inside the first function, so it can get sloppy quick.

Post a Comment for "Calling A Function After Another Function's Callbacks Are Returned"