Comunidad de diseño web y desarrollo en internet online

Controlar cuando terminan dos Event.COMPLETE

Citar            
MensajeEscrito el 23 Abr 2012 04:34 pm
Buenas tardes,

Tengo la siguiente clase de acceso a base de datos, que entre otras cosas, accede para obtener el nombre del archivo de estilos que va a utilizar en un TextField y para obtener de un archivo XML el texto a mostrar con dichos estilos.

Cuando cargo el xml le asigno un listener Event.COMPLETE y hago lo mismo cuando cargo los estilos.

Quiero que una vez se hayan cargado tanto el xml como los estilos, entonces y solo entonces en la función getFicha, guardar los valores en el array para poder devolverlos.

Tal cual está ahora, las variables sheet y xml me vuelven como null

¿Cómo puedo solucionar esto?

Gracias! Un saludo

Código ActionScript :

public class FichaDAL {
   private static var ficheroBD : String = Constantes.NOMBRE_BD;
   private static var dbFile : File = File.applicationStorageDirectory.resolvePath(ficheroBD);
   private static var alerta : Alerta = Alerta.getAlerta();
   private static var loader : URLLoader;
   private static var sheet : StyleSheet;
   private static var xml : XML;
   
   public static function getFicha(idFicha : String) : Array {
      // Obtener los datos de ficha de la tabla
      var stmt : SQLStatement = new SQLStatement();
      var conn : SQLConnection = new SQLConnection();
      var sqlString : String;
      var result : SQLResult;
      var numLineas : uint;
      
      conn.open(dbFile, SQLMode.READ);
      stmt.sqlConnection = conn;
      sqlString = "SELECT fichaId, nombreFicha, estilos, texto " +
               "FROM ficha " +
               "WHERE fichaId = :identificador";
               
      stmt.text = sqlString;
      stmt.parameters[":identificador"] = idFicha;
      
      try {
         // Ejecutar statement
         stmt.execute();
         
         // Acceder a los datos devueltos
         result = stmt.getResult();
         numLineas = result.data.length;
         
         obtenerEstilos(result.data[0].estilos);
         getXML(result.data[0].texto);
         
         // AQUÍ quiero que no se cree el array hasta que no hayan terminado los listeners de las
         // funciones obtenerEstilos y getXML
         var ficha : Array = new Array (result.data[0].fichaId, result.data[0].nombreFicha, sheet, xml);
         
      }catch (error : SQLError) {
         throw new ErrorPIVI(error.name + error.message  + error.details);
      }
      
      return ficha;
   }
   
   public static function instalarFichas() : void {
      var conn : SQLConnection = new SQLConnection();
      var sqlString : String;
      var fichaId : String = "37";
      var nombreFicha : String  = "Texto";
      var estilos : String = "estilos.css";
      var texto : String = "archivo.xml";
      
      var insertaStmt : SQLStatement = new SQLStatement();
      
      try {
         conn.open(dbFile, SQLMode.UPDATE);
         sqlString = "INSERT INTO ficha(fichaId, nombreFicha, estilos, texto) " + 
                  "VALUES (:fichaId, :nombreFicha, :estilos, :texto)";
                  
         insertaStmt.sqlConnection = conn;
         insertaStmt.text = sqlString;
         insertaStmt.parameters[":fichaId"] = fichaId;
         insertaStmt.parameters[":nombreFicha"] = nombreFicha;
         insertaStmt.parameters[":estilos"] = estilos;
         insertaStmt.parameters[":texto"] = texto;
         
         insertaStmt.execute();
      }catch (error:SQLError) {
         throw new ErrorPIVI(error.name + error.message + error.details);
      }
   }

   private static function obtenerEstilos(estilos : String) : void {
      var req : URLRequest = new URLRequest(estilos);
      loader = new URLLoader();
      loader.addEventListener(Event.COMPLETE, estilosCargados);
      loader.load(req);
   }
   
   private static function estilosCargados(event : Event) : void {
      loader.removeEventListener(Event.COMPLETE, estilosCargados);
      sheet = new StyleSheet();
      sheet.parseCSS(loader.data);      
   }
   
   private static function getXML(miArchivo : String) : void {
      var loader : URLLoader = new URLLoader();
      var req : URLRequest = new URLRequest(miArchivo);
      loader.load(req);
      loader.addEventListener(Event.COMPLETE, xmlCargado);
   }
   
   private static function xmlCargado (event : Event) : void {
      var loader : URLLoader = event.target as URLLoader;
      if (loader != null) {
         xml = new XML(loader.data);
      }
   }   
}
}

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 23 Abr 2012 05:15 pm
No se si entiendo bien, pero pongámoslo así. necesitas saber que dos operaciones asincrónicas han terminado para ejecutar una tercera. Hay muchas maneras, lo usual es un pool de llamadas, pero vamos por algo casero para que se entienda. La idea sería usa dos variables booleanas, una para cada carga y si son true llamar la función, es decir en ambos callbacks de la carga algo así

cargado1 = true
if(cargado1 && cargado2) chequearDatos();

Simple pero efectivo (por lo menos mientras sean pocas llamadas)

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 23 Abr 2012 08:43 pm
Pero sigo teniendo el mismo problema, porque cuando entra a la función, cargado1 y cargado2 son false y para cuando las operaciones asíncronas han terminado y cambian el valor de los booleanos, ya ha pasado por el if(cargado1 && cargado2) :S

Gracias

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 23 Abr 2012 08:45 pm
Solo puedes hacerlo en chequearDatos() o la función que has especificado como callback, un if suelto por ahi no entra en este flujo

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos



Ultima edición por solisarg el 24 Abr 2012 04:03 pm, editado 1 vez

Argentina

firefox
Citar            
MensajeEscrito el 23 Abr 2012 09:39 pm
Ok, no te había entendido bien! Ya lo he cambiado como me has indicado, pero ahora tengo otro problemilla, después de tener los dos archivos cargados (y sus indicadores a true), añado los nuevos datos en un Array que será el que tenga todos los datos necesarios. Una vez estén todos estos datos cargados, necesito devolver el array, y había pensado hacerlo mediante un dispatchEvent en la funcion ficherosCargados(), pero no puedo hacerlo aquí puesto que la función es static y por lo que veo dispatchEvent no funciona en static.

¿Hay alguna opción de hacer algo así? El método tiene que ser static puesto que la funcion getFicha() lo es (y tiene que serlo) y esto desencadena que todos los atributos aquí tratados tengan que serlo.

Código ActionScript :

private static function estilosCargados(event : Event) : void {
   loader.removeEventListener(Event.COMPLETE, estilosCargados);
   sheet = new StyleSheet();
   sheet.parseCSS(loader.data);
   fichaDevuelta[2] = sheet;
   trace( "fichaDevuelta[2] " + fichaDevuelta[2]);

   cargadoCSS = true;
   if (cargadoCSS && cargadoXML) {
      ficherosCargados();
   }
   
}

private static function xmlCargado (event : Event) : void {
   //loader.removeEventListener(Event.COMPLETE, xmlCargado);
   var loader : URLLoader = event.target as URLLoader;
   if (loader != null) {
      xml = new XML(loader.data);
      fichaDevuelta[3] = xml;
      trace( "fichaDevuelta[3] " + fichaDevuelta[3]);
   }
   cargadoXML = true;
   if (cargadoXML && cargadoCSS) {
      ficherosCargados();
   }
}

private static function ficherosCargados() : void {
   trace( "******* ARRAY ********* " + fichaDevuelta);
   dispatchEvent(new Event(FICHEROS_CARGADOS));
}


Gracias Jorge

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 23 Abr 2012 09:46 pm
He hecho una modificación y parece ser que así funciona, en lugar de utilizar dispatchEvent, he hecho directamente el return del array con todos los datos cargados, de modo que ahora mi función ficherosCargados() queda de la siguiente manera:

Código ActionScript :

      private static function ficherosCargados() : Array {
         //dispatchEvent(new Event(FICHEROS_CARGADOS));
         return fichaDevuelta;
      }


Gracias por todo

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 01:21 pm
Tip: pon una propiedad publica que indique si los datos han sido cargados o no, sino te arriesgas a recibir null por haber consultado antes de que la carga se complete

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 24 Abr 2012 01:46 pm
Sí, a esta función sólo se llama cuando los dos booleanos que controlan la carga están a true

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 04:02 pm
Acabo de ver a qué te referías, me ha salido error!!!

La situación es esta: tengo una clase FichaDAL con un método getFicha que es el encargado de cargar el fichero xml y el css. Y es aquí donde tengo los booleanos:

Código ActionScript :

      private static var cargadoXML : Boolean = false;
      private static var cargadoCSS : Boolean = false;
      public static var archivosCargados : Boolean = false;

Este archivosCargados es el que he utilizado para poner a true cuando los otros dos booleanos sean true.

Luego tengo otra clase llamada Ficha que en su constructor tiene la siguiente llamada

Código ActionScript :

FichaDAL.getFicha(1) as Array;
.

El problema es que cuando desde la clase documento intento hacer:

Código ActionScript :

         var tf : TextField = new TextField();
         tf.multiline = true;
         var ficha : Ficha = new Ficha();
         tf.styleSheet = ficha.estilos;
         tf.htmlText = ficha.texto.titulo;
         addChild(tf);


Me sale el error

Código :

Error #1009: Cannot access a property or method of a null object reference.
porque no ha terminado de cargarse.

En FichaDAL he añadido la propiedad pública booleana que me sugerías, pero ... si después de hacer

Código :

var ficha : Ficha = new Ficha();
hago

Código ActionScript :

if(ficha.archivosCargados)
, voy a tener el mismo problema puesto que todavía no he dado lugar a que cambie este booleano.

Muchísimas gracias por la ayuda.

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 04:06 pm
Ok, dos cosas

- Hace un método que depende de una carga de info como estático es un error conceptual (carga de info externa ya implica una instancia) y práctico (no puedes usar propiedades porque no tienes instancia)
- Dado que el método es asíncrono, lo suyo es generar un evento con la info necesaria y desde fuera instanciar la clase y agregarle un listener para ese evento

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 24 Abr 2012 04:13 pm
El problema es que el método getFicha() (encargado del acceso a los ficheros) lo había declarado estático únicamente porque esta en una clase de acceso a datos o persistencia que no quería que se puedan crear instancias sino invocar directamente a través de la clase FichaDAL.getFicha(). El problema es que todas las variables que se utilizan en este método tengo que declararlas estáticas para que puedan ser accesibles por el mismo y de ahí el no poder lanzar el evento. Así que tengo un dilema porque siempre he estado acostumbrada a trabajar separando las clases de acceso a datos haciéndolas estáticas pero necesito lanzar ese evento.

¿Sugieres entonces que no haga la clase estática? No estoy acostumbrada a trabajar con métodos asíncronos y me esta volviendo loca.

Muchas gracias de nuevo

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 04:26 pm
Las clases de acceso a datos (o DAO porque supongo vienes de Java) en general se usan desde un modelo que es un Singleton, no estático, de esta forma te aseguras la consistencia de los datos a lo largo de tu aplicación. Se podría usar una clase estática, pero en general entorpece la comunicación si la carga es dinámica y asincrona, sino sería perfecto.
Para entregar un singleton se chequea en el constructor que no exista una instancia (ya que AS3 no tiene constructores privados) y sino se devuelve la instancia ya creadas, hay muchos POST al respecto

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 24 Abr 2012 04:57 pm
Buena apreciación lo del patrón de diseño Singleton, aunque tengo otro error, pongo cómo tengo las clases actualmente:
Clase FichaDAL (Singleton) y encargada de lanzar el evento cuando los ficheros están cargados:

Código ActionScript :

public class FichaDAL extends MovieClip {
   // Siguiendo el patrón de diseño Singleton
   private static var instancia : FichaDAL;
   private static var instanciaPermitida : Boolean;
   // Fin de singleton
   private static var ficheroBD : String = prop.getPropiedad(Constantes.NOMBRE_BD);
   private static var dbFile : File = File.applicationDirectory.resolvePath(ficheroBD);
   
   private var loader : URLLoader;
   private var sheet : StyleSheet;
   private var xml : XML;
   private var cargadoXML : Boolean = false;
   private var cargadoCSS : Boolean = false;
   
   private var fichaDevuelta : Array;
   public const FICHEROS_CARGADOS : String = "ficherosCargados";
   public var archivosCargados : Boolean = false;
   
   public function FichaDAL() : void {
      if (!instanciaPermitida) {
         throw new ErrorPIVI("Debes usar getInstancia");
      }else {
         trace( "Se inicializó una instancia de FichaDAL");
      }
   }
   
   public static function getInstancia() : FichaDAL {
      if (instancia == null) {
         instanciaPermitida = true;
         instancia = new FichaDAL();
         instanciaPermitida = false;
      }else {
         trace( "Se devuelve la instancia existente");
      }
      return instancia;
   }
   
   
   public function getFicha(idFicha : int) : void {
      var stmt : SQLStatement = new SQLStatement();
      var conn : SQLConnection = new SQLConnection();
      var sqlString : String;
      var result : SQLResult;
      var numLineas : uint;
      
      conn.open(dbFile, SQLMode.READ);
      trace( "La base de datos se ha abierto correctamente");
      stmt.sqlConnection = conn;
      sqlString = "SELECT idFicha, nombre, estilos, texto, pdf, idArea " +
               "FROM FICHA " +
               "WHERE idFicha = :identificador";
               
      stmt.text = sqlString;
      stmt.parameters[":identificador"] = idFicha;
      
      try {
         // Ejecutar statement
         stmt.execute();
         
         // Acceder a los datos devueltos
         result = stmt.getResult();
         numLineas = result.data.length;
         
         // Solo nos va a devolver un registro
         trace( "idFicha: " +  result.data[0].idFicha);
         trace( "nombre: " + result.data[0].nombre);
         trace( "estilos: " + result.data[0].estilos);
         trace( "texto: " + result.data[0].texto);
         trace( "pdf: " + result.data[0].pdf);
         trace( "idArea: " + result.data[0].idArea);
      
         obtenerEstilos(result.data[0].estilos);
         getXML(result.data[0].texto);

         fichaDevuelta = new Array (result.data[0].fichaId, result.data[0].nombre);
         fichaDevuelta[4] = result.data[0].pdf;
         fichaDevuelta[5] = result.data[0].idArea;
         
      }catch (error : SQLError) {
         throw new ErrorPIVI(error.name + error.message  + error.details);
      }
   }

   private function obtenerEstilos(estilos : String) : void {
      var req : URLRequest = new URLRequest(estilos);
      loader = new URLLoader();
      loader.addEventListener(Event.COMPLETE, estilosCargados);
      loader.load(req);
   }
   
   private function estilosCargados(event : Event) : void {
      loader.removeEventListener(Event.COMPLETE, estilosCargados);
      sheet = new StyleSheet();
      sheet.parseCSS(loader.data);
      fichaDevuelta[2] = sheet;
      trace( "fichaDevuelta[2] " + fichaDevuelta[2]);

      cargadoCSS = true;
      if (cargadoCSS && cargadoXML) {
         ficherosCargados();
      }
      
   }
   
   private function getXML(miArchivo : String) : void {
      var loader : URLLoader = new URLLoader();
      var req : URLRequest = new URLRequest(miArchivo);
      loader.load(req);
      loader.addEventListener(Event.COMPLETE, xmlCargado);
   }
   
   private function xmlCargado (event : Event) : void {
      var loader : URLLoader = event.target as URLLoader;
      if (loader != null) {
         xml = new XML(loader.data);
         fichaDevuelta[3] = xml;
         trace( "fichaDevuelta[3] " + fichaDevuelta[3]);
      }
      cargadoXML = true;
      if (cargadoXML && cargadoCSS) {
         ficherosCargados();
      }
   }
   
   private function ficherosCargados() : Array {
      trace( "******* ARRAY ********* " + fichaDevuelta);
      trace( "Array " + fichaDevuelta);
      archivosCargados = true;
      dispatchEvent(new Event(FICHEROS_CARGADOS));
      return fichaDevuelta;
   }


La clase Ficha.as (desde la que se invoca a FichaDAL) y escucha su evento:

Código ActionScript :

public class Ficha extends Sprite{
   protected var loader : URLLoader;
   protected var sheet : StyleSheet;
   private static var ficha : Array;
   private static var fichaDAL : FichaDAL = FichaDAL.getInstancia();
   
   private var _idFicha : int = 1;
   private var _nombre : String;
   private var _estilos : StyleSheet;
   private var _texto : XML;
   private var _pdf : String;
   private var _idArea : int;
   
   public var archivosCargados : Boolean = false;
   
   public function Ficha() {
      var nombreMetodo : String = "Ficha()";
      fichaDAL.addEventListener(fichaDAL.FICHEROS_CARGADOS, ficherosCargados);
      ficha = fichaDAL.getFicha(1) as Array
      trace("Ficha: " +_idFicha + " " + _nombre + " " + _estilos + " " + _texto + " " + _pdf + " " + _idArea);
   }
   
   private function ficherosCargados(e:Event):void {
      var nombreMetodo : String = "ficherosCargados";
      this._nombre = ficha[1];
      this._estilos = ficha[2];
      this._texto = ficha[3];
      this._pdf = ficha[4];
      this._idArea = ficha[5];
   }
   
   public function get idFicha():int 
   {
      return _idFicha;
   }
   
   public function set idFicha(value:int):void 
   {
      _idFicha = value;
   }
   
   public function get nombre():String 
   {
      return _nombre;
   }
   
   public function set nombre(value:String):void 
   {
      _nombre = value;
   }
   
   public function get estilos():StyleSheet 
   {
      return _estilos;
   }
   
   public function set estilos(value:StyleSheet):void 
   {
      _estilos = value;
   }
   
   public function get texto():XML 
   {
      return _texto;
   }
   
   public function set texto(value:XML):void 
   {
      _texto = value;
   }
   
   public function get pdf():String 
   {
      return _pdf;
   }
   
   public function set pdf(value:String):void 
   {
      _pdf = value;
   }
   
   public function get idArea():int 
   {
      return _idArea;
   }
   
   public function set idArea(value:int):void 
   {
      _idArea = value;
   }


Y por último el fragmento de código desde el que llamo a la clase Ficha:

Código ActionScript :

var tf : TextField = new TextField();
tf.multiline = true;
var ficha : Ficha = new Ficha();

tf.styleSheet = ficha.estilos;
tf.htmlText = ficha.texto.titulo;
addChild(tf);


Y obtengo el mismo error

Código :

Error #1009: Cannot access a property or method of a null object reference.


y me señala como filas donde se produce el error:

Código ActionScript :

tf.styleSheet = ficha.estilos;
tf.htmlText = ficha.texto.titulo;


¿Aún no está bien el tema de los eventos?

Gracias mil!!

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 05:20 pm
Es el huevo o la gallina, no importa cuantas clases pongas de por medio, la que accede a los datos es la que tiene que escuchar el evento. Por ejemplo en la última línea de de ficheros cargados pongo esto:

Código ActionScript :

private function ficherosCargados(e:Event):void {
  (....)
  this.dispatchEvent(e)
]

//luego

var ficha : Ficha = new Ficha();
ficha.addEventListener(fichaDAL.FICHEROS_CARGADOS, ficherosCargados);
function ficherosCargados(){
   var tf : TextField = new TextField();
   tf.multiline = true;
   tf.styleSheet = ficha.estilos;
   tf.htmlText = ficha.texto.titulo;
   addChild(tf);
}


Si no ejecutas la acción en el callback siempre te encontrarás con el mismo error, no importa cuantas clases haya de por medio

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 24 Abr 2012 09:12 pm
He hecho los cambios, la función ficherosCargados de la clase Ficha, ahora es así:

Código ActionScript :

private function ficherosCargados(e:Event):void {
   var nombreMetodo : String = "ficherosCargados";
   this._nombre = ficha[1];
   this._estilos = ficha[2];
   this._texto = ficha[3];
   this._pdf = ficha[4];
   this._idArea = ficha[5];
   this.dispatchEvent(e);
}


Y en la clase documento ahora:

Código ActionScript :

private static var fichaDal : FichaDAL = FichaDAL.getInstancia();
ficha = new Ficha();
ficha.addEventListener(fichaDal.FICHEROS_CARGADOS, ficherosCargados);

private function ficherosCargados(e:Event):void 
{
   var tf : TextField = new TextField();
   tf.width = Number(prop.getPropiedad(Constantes.PANTALLA_ACTIVIDADES_ANCHO));
   tf.height = Number(prop.getPropiedad(Constantes.PANTALLA_ACTIVIDADES_ALTO));
   tf.multiline = true;
   tf.styleSheet = ficha.estilos;
   tf.htmlText = ficha.texto.titulo;
   addChild(tf);
}


Y esta vez el error me da en la línea

Código :

this._nombre = ficha[1];


Mismo error de siempre

Código :

Error #1009: Cannot access a property or method of a null object reference.


Perdón por las molestias Jorge y muchas gracias de nuevo.

Lore

Por lore711

23 de clabLevel



 

chrome
Citar            
MensajeEscrito el 24 Abr 2012 10:38 pm
Mira si ficha tiene atributos, dado que viene del back-end, utiliza un sniffer para inspeccionar el paquete, por ejemplo charlesproxy.com

Jorge

Por solisarg

BOFH

13669 de clabLevel

4 tutoriales
5 articulos

Genero:Masculino   Bastard Operators From Hell Premio_Secretos

Argentina

firefox
Citar            
MensajeEscrito el 25 Abr 2012 10:00 am
Hola de nuevo, la verdad es que no, ficha no tenía atributos porque estaba recogiéndolo mal, creo que la manera correcta de hacerlo es accediendo al target del evento, así que creo que lo correcto en la clase Ficha, cuando quiero dar valor a los atributos de la clase, la función debe ser así:

Código ActionScript :

private function ficherosCargados(e:Event):void {
   var nombreMetodo : String = "ficherosCargados";
   var miFicha : Array = e.target.fichaDevuelta as Array;
   this._nombre = miFicha[1];
   this._estilos = miFicha[2];
   this._texto = miFicha[3];
   this._pdf = miFicha[4];
   this._idArea = miFicha[5];
   this.dispatchEvent(e);
}


Y de esta manera funciona correctamente.

Muchas gracias por tu tiempo y la ayuda prestada Jorge, un saludo

Lore

Por lore711

23 de clabLevel



 

chrome

 

Cristalab BabyBlue v4 + V4 © 2011 Cristalab
Powered by ClabEngines v4, HTML5, love and ponies.