Calling a function after another function's ca

2019-07-25 11:03发布

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_web_service) {
 console.log('callback two');
};

// the async_calls are library calls that i don't own or modify
var x = function () {
  console.log('x is called');
  async_call_one(callback_one); 
  async_call_two(callback_two);
};

var y = function () {
 console.log('y is called');
};

Test:
x();
y();

// prints: 'x is called', 'y is called', 'callback one', 'callback two'
// what i need: 'x is called', 'callback one', 'callback two', 'y is called'

What I did to accomplish this was calling y() inside call_back_two:

var callback_two = function (result_from_web_service) {
   console.log('callback two');
   y();
};

But now my use case requires that y be called outside of the callback (will be called by the user of my code). i.e. the calling of x() and y() be independent in a way that the calling of x() doesn't end up calling y(). y() should be called independently but only if x() and its callbacks are processed. think of x() as like creating an object in java and y() as a method you would call whenever you want.

//.. some code, x() does some kind of initialization...
x();

// OPTIONALLY call y(), but make sure x() and its callbacks are processed
y();

I tried the below but doesn't work.

 $.when(x()).then(y());

Thanks,

2条回答
疯言疯语
2楼-- · 2019-07-25 11:56

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:

function y () {
    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:

function y (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.

查看更多
再贱就再见
3楼-- · 2019-07-25 12:04

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 () {
    //stuff
    if (finished) {
       y();
    } else {
       finished = true;
    }
}

var aync_call_two = function () {
    //stuff
    if (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.

查看更多
登录 后发表回答