📜 ⬆️ ⬇️

Function Contexts in Action Script

I like to use anonymous functions, pass functions by reference, declare functions directly in the body of another function, etc. This is convenient and practical, but some problems may arise with these mechanisms. Starting from version 9, Flash Player stores its parent functions in this . It sounds simple, but does everyone understand what it means and how hard it was before without it?

For example, you can now describe this function:

 public class TestClass { var property : Number; function updateValue(value : Number) : void { TestClass(this).property = value; } } 

and pass it anywhere:
')
 var func : Function = new TestClass().updateValue; func(555); 

and to be sure, wherever it is called, this will be an instance of the TestClass class. But I'm not talking about that, there are more curious actions that can be performed on functions in Action Script , we will consider them.

Asynchronous calls


Most likely you are faced with the task of calling a remote method on the server and processing the result of this call. Suppose we have a class of service ServerService ServerService , which receives in the constructor a link to a function that must process the response and we perform the typical task of updating the properties of the original object:

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

Everything is written correctly, but why is it so difficult? Let's simplify such nonsense, "smart" code:

 function updateItem(item : SomeObject) : void { new ServerService(onGetResult).getResult(item.startValue); function onGetResult(result : Object) : void { item.endValue = result; } } 

In this case, the onGetResult function has access to all the variables of the updateItem function and its argument item in particular. Such a technique in many cases can reduce the amount of code and remove the negative tinge of asynchrony. By the way, in this onGetResult function onGetResult will no longer be an instance of Example , but simply global .

Multiple asynchronous calls


Even more interesting is the situation when you need to make several asynchronous requests in a loop, and then process each answer accordingly, for example:

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

With this code we will not achieve the desired result. At that moment when the server returns us the answers, the item variable will refer to the last element of the items collection and all data will be assigned only to it, too much honor! In such situations, neither the saved context of the function nor the scope of the parent's variables helps; something else is needed here.

Often, you can use the so-called 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; } } } 

Since the context of the function is not enough to save the item to update it after the server responds, we create a wrapper above the function - a class that we can remember in context everything that is needed. Since the class constructor is still the same function, the item argument will be easily accessible in the onGetResult function.

Standardized ContextFunction Object


In the end, if you do not want to produce a lot of various Loader s, you can enter a universal type - a pattern for repeated use:

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

The essence of the decision is that a ContextFunction instance ContextFunction determined by a function reference with a specific logic and a set of undefined arguments that the function will receive when someone calls it. Also, something else will be added to these arguments, at the request of the calling entity. Consider an example to clarify:

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

This is essentially the same solution as with the Loader , only more universal. The ContextFunction instance stores onGetResult , which will receive a response from the server, as well as a link to the item for which the server value was requested. That is, we, abandoning the context of the function in general, use an instance of an auxiliary class to save the necessary values.

In conclusion, I can assure you that all these tricks are used by me in practice very often and effectively. This is not a problem sucked from the finger.

Source: https://habr.com/ru/post/52458/


All Articles