Вызов функции после возврата обратных вызовов другой функции

#javascript #asynchronous #callback

#javascript #асинхронный #обратный вызов

Вопрос:

Это упрощенная версия моей проблемы:

 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'
  

Для достижения этой цели я вызвал y () внутри call_back_two:

 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());
  

Спасибо,

Комментарии:

1. Предполагая, что вы используете jQuery, если вы хотите использовать $.when() , вам придется использовать Deferred ( api.jquery.com/jQuery. Отложенный ). Что делают обратные вызовы?

2. @juvian $.when не ограничивается вызовами AJAX

3. Если aync_call_one() [так в оригинале] и два оба возвращают отложенные значения, то вы должны быть в состоянии сделать что-то вроде: $.when(aync_call_one(), aync_call_two()).then(//...)

4. вызовы asyn — это вызовы веб-службы, которые возвращают значения в виде обратных вызовов.

Ответ №1:

Единственный способ заставить что-то подобное работать должным образом — сделать y асинхронным. По сути, y внутренне ожидает x завершения перед выполнением своего собственного кода. Это похоже на такие вещи, как domready или onload , которые ожидают, пока произойдут другие действия, прежде чем выполнять свою собственную логику.

Есть два способа добиться этого. Первый, самый простой и наивный способ — это 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 amp;amp; callback) callback();
            callback_one();
        }); 
        async_call_two(function(){
            async_calls --;
            if (async_calls == 0 amp;amp; 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();
  

затем a и b могут выполняться, а могут и не выполняться раньше 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 возвращать обещание, если вы предпочитаете такой стиль.

Ответ №2:

Это немного ближе к спагетти-коду, но вы могли бы объединить два обратных вызова вместе, установив переменные, которые они оба могут видеть, и вызывать 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, вам нужно вернуть объект promise, чтобы использовать $.когда,

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

и внутри асинхронных систем вам нужно сделать

 deferred.resolve();
  

хотя это сработало бы только для одного из ваших асинхронных вызовов, поэтому вам понадобится еще один $.когда внутри первой функции, поэтому она может быстро выйти из строя.

Комментарии:

1. спасибо, но мой вариант использования требует, чтобы я предоставлял y () снаружи -> т.е. я не должен (но я могу) вызывать y внутри функций async_ .

2. Не могли бы вы выполнить var deferred = $.Deferred(); $.when(aync_call_one(), aync_call_two()).then(function () { deferred.success(); }); return deferred.promise(); Редактировать: я не знаю, как редактировать комментарии, поэтому извините за форматирование