Вы можете воспользоваться функцией JS, называемой закрытием. Объедините это с очень распространенным шаблоном JS, называемым «стиль передачи продолжения», и вы получите свое решение. (Ни одна из этих вещей не является оригинальной для JS, но интенсивно используется в JS).
// a function
function foo(some_input_for_foo, callback)
{
// do some stuff to get results
callback(results); // call our callback when finished
}
// same again
function bar(some_input_for_bar, callback)
{
// do some stuff to get results
callback(results); // call our callback when finished
}
«Стиль передачи продолжения» относится к обратному вызову. Вместо того, чтобы возвращать значение, каждая функция вызывает обратный вызов (продолжение) и выдает результат.
Вы можете легко связать их вместе:
foo(input1, function(results1) {
bar(results1, function(results2) {
alert(results2);
});
});
Вложенные анонимные функции могут "видеть" переменные из области видимости, в которой они живут. Поэтому нет необходимости использовать специальные свойства для передачи информации.
Обновление
Чтобы уточнить, во фрагменте кода вашего вопроса ясно, что вы думаете примерно так:
у меня длительный асинхронный
операция, поэтому мне нужно знать, когда это
заканчивается, чтобы начать следующий
операция. Так что мне нужно сделать это
состояние видно как свойство. затем
в другом месте я могу бегать по кругу,
неоднократно исследуя это свойство
посмотрим, когда он изменится на «выполнено»
состояние, поэтому я знаю, когда продолжить.
(И, кроме того, в качестве усложняющего фактора цикл должен использовать setInterval
для запуска и clearInterval
для выхода, чтобы позволить запускать другой код JS - но, тем не менее, это в основном "цикл опроса").
Вам не нужно этого делать!
Вместо того, чтобы заставлять вашу первую функцию устанавливать свойство по завершении, заставьте ее вызывать функцию.
Чтобы сделать это абсолютно понятным, давайте проведем рефакторинг вашего исходного кода:
function cllFnc(tgt) { //!! first function
this.trigger = 0 ;
var trigger = this.trigger ;
var _tgt = document.getElementById(tgt) ; //!! changes the color...
_tgt.style.background = '#66f' ;
alert('Calling! ...') ;
setTimeout(function() { //!! in place of an AJAX call, duration 5000ms
trigger = 1 ;
},5000) ;
}
[ Обновление 2 : Кстати, там есть ошибка. Вы копируете текущее значение свойства trigger
в новую локальную переменную с именем trigger
. Затем в конце вы назначаете 1 этой локальной переменной. Никто другой не сможет увидеть это. Локальные переменные являются частными для функции. Но вам все равно ничего не нужно делать, так что продолжайте читать ... ]
Все, что нам нужно сделать, это сказать этой функции, что вызывать, когда она будет выполнена, и избавиться от установки свойства:
function cllFnc(tgt, finishedFunction) { //!! first function
var _tgt = document.getElementById(tgt) ; //!! changes the color...
_tgt.style.background = '#66f' ;
alert('Calling! ...') ;
setTimeout(function() { //!! in place of an AJAX call, duration 5000ms
finishedFunction(); // <-------- call function instead of set property
},5000) ;
}
Теперь нет необходимости в "проверке вызовов" или специальном помощнике tieExc
. Вы можете легко связать две функции вместе с очень небольшим кодом.
var mySpan = "#myspan";
cllFnc(mySpan, function() { rcvFnc(mySpan); });
Еще одним преимуществом этого является то, что мы можем передавать различные параметры второй функции. При вашем подходе одинаковые параметры передаются обоим.
Например, первая функция может выполнить пару вызовов службы AJAX (для краткости используется jQuery):
function getCustomerBillAmount(name, callback) {
$.get("/ajax/getCustomerIdByName/" + name, function(id) {
$.get("/ajax/getCustomerBillAmountById/" + id), callback);
});
}
Здесь callback
принимает сумму счета клиента, а вызов AJAX get
передает полученное значение функции, которую мы передаем ему, поэтому callback
уже совместим и поэтому может напрямую выступать в качестве обратного вызова для второй вызов AJAX. Так что это сам пример объединения двух асинхронных вызовов в последовательности и их оборачивания в то, что кажется (снаружи) единой асинхронной функцией.
Тогда мы можем связать это с другой операцией:
function displayBillAmount(amount) {
$("#billAmount").text(amount);
}
getCustomerBillAmount("Simpson, Homer J.", displayBillAmount);
Или мы могли бы (снова) использовать анонимную функцию:
getCustomerBillAmount("Simpson, Homer J.", function(amount) {
$("#billAmount").text(amount);
});
Таким образом, объединяя вызовы функций таким образом, каждый шаг может передавать информацию на следующий шаг, как только она становится доступной.
Заставив функции выполнять обратный вызов по завершении, вы освобождаетесь от любых ограничений на то, как каждая функция работает внутри. Он может делать вызовы AJAX, таймеры, что угодно. Пока обратный вызов «продолжения» передается вперед, может быть любое количество уровней асинхронной работы.
По сути, в асинхронной системе, если вы когда-нибудь будете писать цикл для проверки переменной и выяснения, изменилось ли ее состояние, то что-то пошло не так. Вместо этого должен быть способ предоставления функции, которая будет вызываться при изменении состояния.
Обновление 3
Я вижу в других комментариях, что вы упоминаете, что настоящая проблема заключается в кэшировании результатов, поэтому вся моя работа, объясняющая это, была пустой тратой времени. Это то, что вы должны поставить в вопросе.
Обновление 4
Совсем недавно я написал короткое сообщение в блоге на тему кэширования результатов асинхронных вызовов в JavaScript .
(конец обновления 4)
Еще один способ поделиться результатами - предоставить возможность для одного обратного вызова для «трансляции» или «публикации» нескольким подписчикам:
function pubsub() {
var subscribers = [];
return {
subscribe: function(s) {
subscribers.push(s);
},
publish: function(arg1, arg2, arg3, arg4) {
for (var n = 0; n < subscribers.length; n++) {
subscribers[n](arg1, arg2, arg3, arg4);
}
}
};
}
Итак:
finished = pubsub();
// subscribe as many times as you want:
finished.subscribe(function(msg) {
alert(msg);
});
finished.subscribe(function(msg) {
window.title = msg;
});
finished.subscribe(function(msg) {
sendMail("admin@mysite.com", "finished", msg);
});
Тогда пусть какая-нибудь медленная операция опубликует свои результаты:
lookupTaxRecords("Homer J. Simpson", finished.publish);
Когда этот звонок завершится, он будет звонить всем трем абонентам.