Я люблю использовать анонимные функции, передавать функции по ссылке, объявлять функции прямо в теле другой функции и т.п. Это удобно и практично, но с этими механизмами могут возникнуть некоторые проблемы. Начиная с версии 9 Flash Player сохраняет в
Например, теперь можно описать такую функцию:
и передавать её куда угодно:
и быть уверенным, где-бы её не вызвали в
Скорее всего вы сталкивались с задачей вызова удаленного метода на сервере и обработки результата этого вызова. Допустим у нас есть класс сервиса
Всё написано верно, но зачем так сложно? Давайте упростим подобную ерунду, «умным» кодом:
В данном случае функция
Ещё интереснее ситуации когда нужно сделать несколько асинхронных запросов в цикле, а затем обработать каждый ответ соответственно, например:
Данным кодом мы не достигнем желаемого результата. В тот момент когда сервер вернёт нам ответы, переменная
Зачастую можно воспользоваться так называемым
Так как контекста функции недостаточно что-бы сохранить
В конце концов, если вы нежелаете плодить массу всевозможных
Суть решения в том, что экземпляр
Это по-сути то же решение, что и с
В заключение, могу вас уверить, что все эти трюки используются мной на практике очень часто и эффективно. Это не высосанные из пальца проблемы.
this
функции её родителя. Звучит просто, но все ли понимают, что это значит и как тяжело было раньше без этого?Например, теперь можно описать такую функцию:
public class TestClass
{
var property : Number;
function updateValue(value : Number) : void
{
TestClass(this).property = value;
}
}
и передавать её куда угодно:
var func : Function = new TestClass().updateValue;
func(555);
и быть уверенным, где-бы её не вызвали в
this
будет экземпляр класса TestClass
. Но я не об этом, есть более любопытные действия, которые можно производить над функциями в Action Script, их мы их рассмотрим.Асинхронные вызовы
Скорее всего вы сталкивались с задачей вызова удаленного метода на сервере и обработки результата этого вызова. Допустим у нас есть класс сервиса
ServerService
, который принимает в конструктор ссылку на функцию, которая должна обработать ответ и мы выполняем типичную задачу обновления свойства исходного объекта:class Example
{
function updateItem(item : SomeObject) : void
{
_tempObject = item;
new ServerService(onGetResult).getResult(item.startValue);
}
function onGetResult(result : Object) : void
{
_tempObject.endValue = result;
}
private var _tempObject : SomeObject;
}
Всё написано верно, но зачем так сложно? Давайте упростим подобную ерунду, «умным» кодом:
function updateItem(item : SomeObject) : void
{
new ServerService(onGetResult).getResult(item.startValue);
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}
В данном случае функция
onGetResult
имеет доступ ко всем переменным функции updateItem
и к её аргументу item
в частности. Такой прием во многих случаях может сократить объем кода и убрать негативный оттенок асинхронности. Кстати, в this
функции onGetResult
будет уже не экземпляр Example
, а просто global
.Множественные асинхронные вызовы
Ещё интереснее ситуации когда нужно сделать несколько асинхронных запросов в цикле, а затем обработать каждый ответ соответственно, например:
function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ServerService(onGetResult).getResult(item.startValue);
}
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}
Данным кодом мы не достигнем желаемого результата. В тот момент когда сервер вернёт нам ответы, переменная
item
будет ссылаться на последний элемент коллекции items
и все данные присвоятся только ему, слишком много чести! В таких ситуациях не помогает ни сохраняемый контекст функции ни область видимости переменных родителя, тут нужно что-то другое.Зачастую можно воспользоваться так называемым
Loader
-ом:function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ValueLoader(item);
}
}
class ValueLoader
{
public function ValueLoader(item : SomeObject)
{
new ServerService(onGetResult).getResult(item.startValue);
function onGetResult(result : Object) : void
{
item.endValue = result;
}
}
}
Так как контекста функции недостаточно что-бы сохранить
item
для обновления его после ответа сервера, мы создаем над функцией обёртку — класс, которые способен запомнить в контексте всё что нужно. Так как конструктор класса всё та же функция, аргумент item
без проблем будет доступен в функции onGetResult
.Стандартизированый объект ContextFunction
В конце концов, если вы нежелаете плодить массу всевозможных
Loader
-ов, можно ввести универсальный тип — паттерн для многократного использования:class ContextFunction
{
public function ContextFunction(targetFunction : Function, ... args)
{
_contextArgumnets = args;
_targetFunction = targetFunction;
}
public function func(... args) : void
{
var targetArguments : Array =
args.concat(_contextArgumnets);
_targetFunction.apply(this, targetArguments);
}
private var _contextArgumnets : Array;
private var _targetFunction : Function;
}
Суть решения в том, что экземпляр
ContextFunction
определяется ссылкой на функцию с конкретной логикой и набором неопределённых аргументов, которые получит функция, когда её кто-то вызовет. Так же, к этим аргументам добавятся ещё что-то, по желанию вызывающей сущности. Рассмотрим пример для прояснения:function updateItems(items : ArrayCollection) : void
{
for each (var item : SomeObject in items)
{
new ServerService(new ContextFunction(onGetResult, item).func).
getResult(item.startValue);
}
}
function onGetResult(result : Object, item : SomeObject) : void
{
item.endValue = result;
}
Это по-сути то же решение, что и с
Loader
-ом, только более универсальное. Экземпляр ContextFunction
сохраняет onGetResult
, которая получит ответ от сервера, а также ссылку на item
для которого запрашивалось серверное значение. То-есть, мы, отказываясь от контекста функции вообще, используем экземпляр вспомагательного класса, для сохранения нужных значений.В заключение, могу вас уверить, что все эти трюки используются мной на практике очень часто и эффективно. Это не высосанные из пальца проблемы.