Delegando que es gerundio
[Please note that this post was originally posted on July 2006.]
[Este artículo fue publicado en Julio de 2006.]
—
Delegar es una cosa que mola. En la vida en general y en Flash en particular. Sobre todo cuando nos movemos de AS1 a AS2 + clases. Los foros de Flash están llenos de preguntas que tienen que ver con el “scope” o ámbito dentro de los famosos “callbacks” de objetos como XML, LoadVars, XMLSocket, etc. Flash necesita estas funciones porque las llamadas al servidor en Flash son asíncronas, es decir, el player no detiene la ejecución del código cuando se hace, por ejemplo, una petición de un fichero xml:
-
class A{
-
-
private var oneVar:String = "Hello world";
-
-
function A():Void{
-
-
var myXML:XML = new XML();
-
myXML.ignoreWhite = true; // alguien ha encontrado esto en false????
-
-
myXML.onLoad = function():Void{
-
trace(oneVar); // undefined
-
}
-
-
myXML.load("myXML.xml");
-
-
}
-
-
}
Esto pasa porque el ámbito “dentro” del onLoad es el propio objeto myXML, NO la clase. Puedes hacer la prueba haciendo trace(this) dentro del onLoad.
Una de las primeras soluciones que se utilizó para esto fue crear dentro de la función principal una variable que hacía referencia a la propia clase. Algo como esto:
-
class A{
-
-
private var oneVar:String = "Hello world";
-
-
function A():Void{
-
-
var owner = this;
-
var myXML:XML = new XML();
-
myXML.ignoreWhite = true;
-
-
myXML.onLoad = function():Void{
-
trace(owner.oneVar); // yeah!
-
}
-
-
myXML.load("myXML.xml");
-
-
}
-
-
}
Y esto funciona. Este comportamiento “peculiar” de Flash (de los ECMAScript, vamos) se llama closure, algo de lo que yo me enteré en éste post de Domestika. Más información en Scope Chain and Memory waste in Flash MX y en la Wikipedia (para campeones).
La misión era clara, buscar una forma más sencilla y elegante de resolver el problema del ámbito en las llamadas asíncronas. Pues a todo esto llegó la version 7.2 del Flash IDE y el señor Mike Chambers introdujo la clase Delegate. Utilizando esa clase dejaríamos el código anterior en algo como esto:
-
import mx.utils.Delegate;
-
-
class A{
-
-
private var oneVar:String = "Hello world 2";
-
-
function A(){
-
-
var myXML:XML = new XML();
-
myXML.ignoreWhite = true;
-
myXML.onLoad = Delegate.create(this,xmlLoaded);
-
myXML.load("myXML.xml");
-
-
}
-
-
private function xmlLoaded(success:Boolean):Void{
-
trace(oneVar);
-
}
-
-
}
Estamos “delegando” el onLoad en la función xmlLoaded, pero, lo más importante, el ámbito de la función xmlLoaded es la clase original, por lo que “encontramos” la variable sin problemas. Ahora empezamos a molar.
Esto definitivamente NO es lo mismo que hacer: myXML.onLoad = xmlLoaded. Si lo probáis, estaréis con el mismo problema que antes, el ámbito de la función xmlLoaded será el objeto myXML, por lo que el trace volverá a ser undefined.
El mayor problema de la clase de Macromedia (aún era Macromedia) es que NO permite el paso de parámetros a la función delegada, pero pronto llegaron los frikis para solucionarlo haciendo sus propias clases para delegar. La que yo utilizo es una copia con alguna modificación de una que encontré en la lista de MTASC. Con estas nuevas clases se pueden pasar parámetros de la siguiente forma:
-
myXML.onLoad = Delegate.create(this,xmlLoaded,"val1",val2);
OJO, nuestros parámetros llegarán despué de los “oficiales”, en este caso el típico success que llega a los onLoad del objeto XML.
Como curiosidad apuntar que con las clases de Delegate se pueden hacer cosas como esta:
-
import tv.zarate.Utils.Delegate;
-
-
class A{
-
-
function A(){
-
-
// delegamos la funcion en una variable local
-
-
var delegatedOne:Function = Delegate.create(this,getOneValue);
-
// pasamos la funcion delegada como parametro
-
-
doLater(delegatedOne);
-
-
}
-
-
private function doLater(delegated:Function):Void{
-
-
// ejecutamos la funcion pero no devuelve nada
-
trace("Pero no me devuelve nada? " + delegated());
-
-
}
-
-
private function getOneValue():String{
-
-
trace("La funcion se ejecuta");
-
return "I rock \m/";
-
-
}
-
-
}
Decidir si esta manera de trabajar es una best practice o no es cosa de cada uno. Hace poco me encontré con una aplicación MVC en la que el modelo le pasaba a la vista las funciones delegadas para los botones de la interface. A gusto del consumidor.
Lo gracioso de esto es que dentro de poco las clases de delegar no se van a utilizar porque en AS3 la delegación es automática (echad un ojo a AS3 overview, Method closures). Pero vamos, al ritmo que se va adoptando las nuevas versiones, yo creo que este artículo aún puede ser útil a mucha gente.
Pues esto es todo familia, HTH.