在返回另一个函数的回调之后调用函数

时间:2014-07-02 20:05:40

标签: javascript asynchronous callback

这是我的问题的简化版本:

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'

我做的是在call_back_two中调用y():

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

但是现在我的用例要求在回调之外调用y(将由我的代码的用户调用)。即调用x()和y()是独立的,调用x()不会最终调用y()。应该独立调用y(),但仅在处理x()及其回调时。认为x()就像在java中创建一个对象一样,y()作为一个你可以随时调用的方法。

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

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

我尝试过以下但不起作用。

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

谢谢,

2 个答案:

答案 0 :(得分:4)

使这样的工作正常工作的唯一方法是使y异步。基本上,y在执行自己的代码之前在内部等待x完成。这类似于domreadyonload之类的东西,它们在执行自己的逻辑之前等待其他事情发生。

有两种方法可以实现这一目标。第一种,最简单,最天真的方式是setTimeout轮询。让x设置变量或属性,并在执行前检查:

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

第二种方法是创建虚拟事件或承诺。并非所有的promise库都支持使用已经过期的承诺(我自己的承诺),所以您可能需要编写自己的控制流来处理这种情况。为此,您需要重写x以支持事件或类似承诺的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;
})();

现在在y中,您可以使用x.loaded函数在加载x时执行代码:

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

当然,这有使y异步的问题。因此,如果您的用户希望编写如下内容:

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

然后ab可能会或可能不会在y之前执行。要解决此问题,您需要让y接受回调,以便用户可以控制他们的程序流程:

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

或:

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

所以他们必须像这样使用y

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

或者,如果你喜欢这种风格,你可以让y返回一个承诺。

答案 1 :(得分:0)

在意大利面条代码的道路上有一点点但是你可以通过设置它们都可以看到的变量将两个回调结合在一起,并且当它们都返回时只调用y()。

例如

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;
    }
}

但是如果你必须使用jquery promises,你需要返回一个promise对象来使用$ .when,

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

并且你需要做的asyncs内部

deferred.resolve();

虽然这只适用于你的一个异步调用,所以在第一个函数中你需要另外一个$ .when,所以它可以快速变得邋。