2009-01-08 12 views
25

En el siguiente código intento cargar algunas imágenes y ponerlas en escena tan pronto como se carguen de forma individual. Pero está bloqueado porque solo se muestra la última imagen. Sospecho que es un problema de cierre. ¿Cómo puedo arreglarlo? ¿No es el comportamiento de los cierres en AS3 el mismo que en Java Script?Cómo solucionar un problema de cierre en ActionScript 3 (AS3)

var imageList:Array = new Array(); 
imageList.push({'src':'image1.jpg'}); 
imageList.push({'src':'image2.jpg'}); 
var imagePanel:MovieClip = new MovieClip(); 
this.addChildAt(imagePanel, 0); 

for (var i in imageList) { 
    var imageData = imageList[i]; 
    imageData.loader = new Loader(); 

    imageData.loader.contentLoaderInfo.addEventListener(
     Event.COMPLETE, 
     function() { 
      imagePanel.addChild(imageData.loader.content as Bitmap); 
      trace('Completed: ' + imageData.src);    
     }); 

    trace('Starting: ' + imageData.src); 
    imageData.loader.load(new URLRequest(imageData.src)); 
} 
+0

No recomendaría usar la etiqueta javascript cuando no tiene mucha relevancia, así que la eliminé. – Salty

Respuesta

45

no es el comportamiento de los cierres en AS3 los mismos que en la escritura de Java?

Sí, JavaScript hace exactamente lo mismo. Como lo hace Python. Y otros.

Aunque defina 'var imageData' dentro de 'for', para los bucles no introduzca un nuevo alcance en estos idiomas; de hecho, la variable imageData está vinculada en el ámbito contenedor (la función externa, o en este caso, parece ser un alcance global). Puede verificar esto mirando imageData después de que el ciclo haya terminado de ejecutarse, y encuentre el último elemento de imageList en él.

De modo que solo hay una variable imageData, no una para cada iteración del ciclo. Cuando se COMPLETE incendios, ingresa al cierre y lee cualquier valor imageData tiene ahora, no en el momento en que se definió la función (*). Normalmente, el for-loop habrá terminado por el punto COMPLETE fuegos e imageData retendrá ese último elemento de la iteración final.

(* - existen 'principios vinculantes' lenguajes que se evaluar el valor de la variable en el punto que define un cierre Pero ActionScript no es uno de ellos..)

Posibles soluciones tienden a implicar el uso de una función externa para introducir un nuevo alcance. Por ejemplo:

function makeCallback(imageData) { return function() { 
    imagePanel.addChild(imageData.loader.content as Bitmap); 
    trace('Completed: ' + imageData.src);                          
} } 
... 
imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData)); 

Usted/CAN/poner esto en línea, pero la función doblemente anidado() empieza a ser más difícil de leer.

Consulte también Function.bind() para una función de aplicación parcial de función general que podría utilizar para lograr esto. Es probable que sea parte de futuras versiones de JavaScript/ActionScript, y se puede agregar al lenguaje a través de prototipos mientras tanto.

+0

Gracias por aclarar las cosas. Así que, de hecho, el comportamiento es diferente al de Java Script, ya que Java Script es un lenguaje 'de enlace inicial', que en mi opinión es mejor. ¿Por qué uno preferiría el comportamiento de AS3? – fromvega

+0

No, ActionScript y JavaScript se comportan igual, tienen que hacerlo, ya que ambos cumplen con el estándar ECMAScript. Tampoco lo son los lenguajes de enlace temprano, que tienden a aparecer más en la programación funcional. Me encantaría ver un lenguaje de enlace temprano basado en un lenguaje de script moderno. – bobince

+0

¡Tienes razón! Realizo algunas pruebas y, de hecho, el comportamiento es el mismo. Creo que pensé que era diferente porque nunca me encontré con una situación con este tipo de retraso en Javascript. ¡Gracias! – fromvega

1
(function() { 
    var imageData = imageList[i]; 
    imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function() { 
    // use imageData; 
    }); 
}).apply(); 
2

Uso del estilo más funcional para cada método de la clase Array evita este problema. Esto ya se ha mencionado, pero lo ampliaré aquí.

imageList.forEach(function (item:MovieClip, index:int, list:Array) { 
    // add your listener with closure here 
}) 

Usando este método, la función pasa a la forEach define un nuevo ámbito de aplicación cada iteración. ahora puede agregar un cierre dentro de este alcance y recordará cada instancia como desee.

En una nota relacionada:

Typing esos 3 argumentos todo el tiempo es un dolor tan ... También puede hacer que menos/más fea con una función de adaptador:

// just give me the item please 
imageList.forEach (itrAdpt(function (image: ImageData) { 
    // add your listener with closure here 
})) 

// give me the item and it's index 
imageList.forEach (itrAdpt(function (image: ImageData, index:int) { 
    // add your listener with closure here 
})) 

// give me the item, index and total list length 
imageList.forEach (itrAdpt(function (image: ImageData, index:int, length:int) { 
    // add your listener with closure here 
})) 

donde itrAdpt es una función (posiblemente mundial) define algo como:

public function itrAdpt(f: Function): Function 
{ 
    var argAmount:int = f.length 

    if (argAmount == 0) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element) 
     } 
    } 
    else if (argAmount == 1) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element) 
     } 
    } 
    else if (argAmount == 2) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element, index) 
     } 
    } 
    else if (argAmount == 3) 
    { 
     return function (element:*, index:int, colection:*):* { 
      return f(element, index, colection.length) 
     } 
    } 
    else 
    { 
     throw new Error("Don't know what to do with "+argAmount+"arguments. Supplied function should have between 1-3 arguments") 
    } 
} 
1

Si la creación de una función llamada no apelar a usted, la respuesta de bobince se puede convertir en esto sin mucho sacrificio a la legibilidad:

var makeCallback = function(imageData:String) 
    { 
    return function(evt:Event) 
    { 
     imagePanel.addChild(imageData.loader.content as Bitmap); 
     trace('Completed: ' + imageData.src); 
    } 
    } 

... 

imageData.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, makeCallback(imageData)); 

sólo mi preferencia, su experiencia puede variar.

Cuestiones relacionadas