flexunit addAsync chaining - actionscript-3

For some reason, addAsync chaining in a flexunit test as described in this article utterly fails to work when I try to do it.
public function testWhatever():void {
var cont:EventDispatcher = new EventDispatcher();
cont.addEventListener("continue", addAsync(verifyFirst, 1000));
cont.dispatchEvent(new Event("continue"));
}
private function verifyFirst(e:Event):void {
var cont:EventDispatcher = new EventDispatcher();
cont.addEventListener("continue", addAsync(verifySecond, 1000));
cont.dispatchEvent(new Event("continue"));
}
private function verifySecond(e:Event):void {
assertTrue(true);
}
If I run this test, verifyFirst gets called but verifySecond does not. I'm assuming this is a bug in flexunit ... is there a workaround?

I did some more research and found that this is indeed a bug in flexunit, which looks to be fixed in the next release. The workaround I found was to instead use Application.application.callLater to dispatch the second event.
private function verifyFirst(e:Event):void {
var cont:EventDispatcher = new EventDispatcher();
cont.addEventListener("continue", addAsync(verifySecond, 1000));
Application.application.callLater(cont.dispatchEvent,
[new Event("continue")]);
}
This question was inspired by an attempt to inspect the state of an object after everything in Flash's event queue had been processed. I discovered a simpler way to accomplish this without messing with EventDispatchers.
Make the following call the end of the first part of the test when you want the event queue to be processed.
Application.application.callLater(addAsync(phaseTwo, 1000, [args...]), [null]);
With the phaseTwo function having the following signature.
private function phaseTwo(e:Event, args:Array):void
e will be passed a null object. This is necessary to be compatible with addAsync.

Related

closures with popups using flex 4.6

I have this custom event handler that shows a popup and accepts input from the user:
private var mySkinnablePopupContainer:MySkinnablePopupContainer;
private function handleShowGridPopupEvent(event:ShowGridPopupEvent):void {
var mouseDownOutSideHandler:Function = function(mdEvent:FlexMouseEvent):void {
// At this point, event.targetControl contains the wrong object (usually the previous targetControl)
if (mdEvent.relatedObject != event.targetControl) {
mySkinnablePopupContainer.close();
}
}
var gridPopupSelectionHandler:Function = function(popEvent:PopUpEvent):void {
if (!popEvent.commit) return;
// At this point, event.targetData contains the wrong object (usually the previous targetData)
myModel.doSomethingWithData(popEvent.data.selectedItem, event.targetData);
}
if (!mySkinnablePopupContainer) {
mySkinnablePopupContainer = new MySkinnablePopupContainer();
mySkinnablePopupContainer.addEventListener(PopUpEvent.CLOSE, gridPopupSelectionHandler);
mySkinnablePopupContainer.addEventListener(FlexMouseEvent.MOUSE_DOWN_OUTSIDE, mouseDownOutSideHandler);
}
// At this point, event.targetData contains the correct object
mySkinnablePopupContainer.dataProvider = getMyDPArrayCollection(event.targetData);
mySkinnablePopupContainer.open(this);
var point:Point = event.targetControl.localToGlobal(new Point());
mySkinnablePopupContainer.x = point.x + event.targetControl.width - mySkinnablePopupContainer.width;
mySkinnablePopupContainer.y = point.y + event.targetControl.height;
}
Every time the function handler gets called, it will have the correct ShowGridPopupEvent object but by the time it calls the
gridPopupSelectionHandler, it will contain the old object from a previous call. It works the first time, subsequent calls fails.
Somehow the reference to the event object changed somewhere in between before opening the popup and after.
Any idea what am I doing wrong here? Is this a bug with flex?
found the prob. since im attaching listener only once, it will reference the old listener, with the reference to the old data. i guess i was expecting its reference to be updated whenever i create the closure. not in this case. possible fix is to remove the listener and re-add it again but I abandoned the idea of using closures, and aside from what RIAStar mentioned, it is also impractical as it only gives more overhead by creating a new function for every invocation of the handler.

What is wrong with this as3 piece of code?

I'm an as3 newbie and I'm trying to send a message through netstream to a function that will handle it.
Here is the line that sends
public function _attachLocalVideoAndAudio(muteCam:Boolean = false, muteMic:Boolean =
false):void
{
...
_outgoingStream.send("flagVideo",true);
...
}
public function flagV():Boolean
{
var client:Object;
client.flagVideo=function(flag:Boolean):void{
check=flag;
}
return check;
}
*check is defined
I'm using Monster Debugger to debug and it seems that there is a problem with the inner function that handles the flag. There isn't much tutorials on the web on netstream.send() but I wrote this code based on something I saw online.

Why does a closed NetConnection that has no event listeners or references stick around in memory?

It seems that if flash.net.NetConnection is instantiated and connected to an HTTP URL (such as an AMFPHP gateway), that instance is never picked up by garbage collection even after it has been closed and the only reference is set to null.
On the other hand, if the instance is connected to null (as would be done when used to play video/mp3 files), the instance is cleared from memory.
To clarify, the following connection will stick around in memory:
var stickyConn:NetConnection = new NetConnection();
stickyConn.connect("http://myserver/amfphp/gateway.php");
stickyConn.close();
stickyConn = null;
Whereas, the following connection will be cleared from memory immediately:
var tempConn:NetConnection = new NetConnection();
tempConn.connect(null);
tempConn.close();
tempConn = null;
Some things I have already tried to solve this issue:
set the client to an empty object (since the default value of the client is the NetConnection itself)
before closing the connection, call connect(null)
after closing the connection, call connect(null) and close it again
Has anyone run into this issue before? Is there a solution to this?
I have built heavyloaded FLV/Mp4 Players using AS3 quite often. When I am using a service like Akamai or Adobe's internal NetConnection Class I always keep in mind the
client object.
the is the property of NetConnection on which ALL callback methods are invoked. The default is this NetConnection instance this. If you set the client property to another object, callback methods will be invoked on that object.
In this way you can easily understand how Garbage Collection was never really applied accross each component in the same way. So, where stickyConn = null; only stops the playback, since you never declared a Weak Reference, Garbage Collection has no clue what to look for.
I have had success with differrent methods based on the specific player:
Simply stating NetConnectionObj.client = this usually suffices. But what if your NetConnection is extended or implementing an interface? Simply use a null Dictionary object:
var d:Dictionary = new Dictionary(true); . From here Garbage collection will recognize "d" as a weak reference and automatically dump it;
Hence, your snippet will look somewhat like this:
var Dc:Dictionary = new Dictionary(true);
NetConnection:NetConnection.client = Dc;
or some variation with the same intent.
I know this works, so reach out if you need help...
I may have been vague with the last answer in regards to GC and Dictionary Objects. Please take this snippet into consideration. I wrote it quickly but I try to explain the Concept of what solves your problem; mainly since I have dealt with it before:
public class Main extends MovieClip {
private var connection:NetConnection;
private var __nData:*;
private var _instance:*;
private var _closure:Function;
private var _D:Dictionary;
public function Main() {
connection = new NetConnection();
connection.addEventListener(NetStatusEvent.NET_STATUS, _nsHandle)
connection.connect(null);
}
public function _nsHandle(event:NetStatusEvent):void {
try {
connection = new NetConnection();
connection.connect(null);
connection.client = RegisterForGC(event.target);
RegisterForGC(connection);
} finally {
__nData = event.target.netConnection;
}
}
public function RegisterForGC(NCObject:*):* {
_instance = NCObject;
_closure = function ():void {}
_listener = function (e:Event):void {}
_D = new Dictionary(true);
_D[_listener] = "A";
_D[_instance] = "B";
_D[_closure] = "C";
try {
new LocalConnection().connect( "A" );
new LocalConnection().connect( "B" );
} catch (anything:*) { }
return _instance;
}
}
I'm not sure but Your example seems to suggest you are declaring your vars on stage / frame.
close(); is all you need for this to work HOWEVER....
from what I have found with NetConnection it for some reason unless all vars / functions are declared in an External class eg. public vars public function,
It stays in memory even after using close();
Pulled out my hair figuring this out with an audio streaming project.
However once I moved all coding to an external class, close(); actually closed the connection.
If your code is on a frame on stage or within MC I would create a class and declare vars & functions in said External Class.as and for some stupid reason it works.
Hope this helps.
Are you using a NetStream object and not disposing of it when finished? I only ask because I rarely see a NetConnection without a NetStream object far behind it.

remove ENTER_FRAME EventListener from inside this as3

This is my code in Flash/AS3, in main class.
addEventListener(Event.ENTER_FRAME,function(e:Event){
if(findObject == true){
// I want to remove this ENTER FRAME
}
});
try this:
e.currentTarget.removeEventListener(e.type, arguments.callee)
You shouldn't be doing what you do in the code above.
The mgraph's code has a tiny chance of failing to work as advertised if the currentTarget of the event doesn't have a removeEventListener() method (possible, but very unlikely). From the compiler standpoint though you will be trying to dynamically resolve the method on a generic object which is error prone and should be handled with care. This is hazardous because it shows that the programmer "did not know" what kind of object was she expecting to handle and worked by assumption. Assumptions are great for finding a solution but are equally bad for implementing one.
If you thought of optimizing something in the way you did it, then, just FYI this actually creates a unique (redundant) name in the symbol table (in the compiled SWF file) which causes worse compression of the SWF.
If you are doing this as a matter of experiment, this is fine, but you should avoid such code in real life projects.
One more thing to be aware of: comparison to true constant is 100% useless. If such comparison makes any sense at all (i.e. findObject may evaluate to false any time), then if (findObject) { ... } is equivalent but shorter version of your code.
Last thing, hopefully, the anonymous function is missing return type declaration. It won't really change much in your example, except that you will get compiler warning. Omitting type declaration is, in general, a bad style.
EDIT
public function addEventListener(type:String, listener:Function ...):void
{
this._listeners[type].push(listener);
}
public function dispatchEvent(event:Event):void
{
for each (var listener:Function in this._listeners[event.type])
listener(event);
}
public function removeEventListener(type:String, listener:Function, ...):void
{
delete this._listeners[type][listener];
}
Suppose you actually want to implement IEventDispatcher (instead of using another EventDispatcher - you may have your reasons to do so, one such reason is that native EventDispatcher generates insane amounts of short-lived objects - events, and you may want to reduce that.) But there is no way you can replicate event.target or event.currentTurget in your code because you can't access the object owning the method, so, you would leave that out.
Another example:
public class SomeEvent extends Event
{
private var _target:NotEventDispatcher;
public function SomeEvent(type:String, someTarget:NotEventDispatcher)
{
super(type);
this._target = someTarget;
}
public override function get target():Object
{
return this._target;
}
}
This is something that I actually saw in real world, this was used in either Mate or similar framework to sort of "anonymously" connect all event dispatchers to a single static instance of some "mothership event dispatcher".
I don't necessarily justify this approach, but, technically, nothing stops you from doing either one of these. What I was saying in my post above is that in certain situations the language promises you things, like, if you did:
var dispatcher:IEventDispatcher;
try
{
dispatcher = IEventDispatcher(event.currentTarget);
// now you can be sure this object has removeEventListener
dispatcher.removeEventListener(event.type, arguments.callee);
}
catch (error:Error)
{
// but what are you going to do here?
}
But the most common case would be you subscribing to a bubbling event, in which case, you don't know whether you want to unsubscribe from event.target or event.currentTtarget - because you don't know which one is that you are listening to.
I agree with wvxvw.
Another way to approach your problem is to have a variable to control the "state" of your ENTER_FRAME event:
private var _state:String;
private function init(e:Event):void {
addEventListener(Event.ENTER_FRAME, loop, false, 0, true);
}
private function loop(e:Event):void {
switch(_state) {
case "play":
// do play stuff
// when you want to pause
// goToPause();
break;
}
}
// you can call the method below from a button or whatever you want
private function goToPause():void {
_state = "pause";
// do some stuff here
// when you are done, switch "_state" back to "play"
}
In this example, you keep listening for ENTER_FRAME, but it only does things when the _state variable is set to "play". You can also remove the event listener in the goToPause method:
private function goToPause():void {
_state = "pause";
removeEventListener(Event.ENTER_FRAME, loop);
}
However, the nice thing about using the "_state" to switch things is that you don't end up having a mess of addEventListeners and removeEventListeners (which is what can happen depending on how complicated your loop gets) that you have to keep track of.
You should not use anonymous function call if you would like to remove listener some time later.
public function main():void
{
//...
//some method, where you add event listener
//...
//adding enterFrame event listener
this.addEventListener(Event.ENTER_FRAME,enterFrameHandler);
//...
}
private function enterFrameHandler(e:Event)
{
if(findObject) // " == true" is not really necessary here.
{
// removing enterFrame listener:
this.removeEventlistener(Event.ENTER_FRAME,enterFrameHandler);
}
}
Just for a completeness with the other techniques mentioned here, the function you are creating is a unbound closure, so you can also leverage that concept to reference both your function and dispatcher.
var callback:Function;
var dispacher:IEventDispatcher = this;
addEventListener(Event.ENTER_FRAME, callback = function(e:Event){
if(findObject == true){
dispacher.removeEventListener(Event.ENTER_FRAME, callback);
}
});
Normal closed-over variable rules apply.

Clearing eventListeners on a FileReference object

I have a strange issue! I am trying to remove an event listener on a FileReference object by calling a function, but it seems not to be removed, and I do not understand why.
Here is the code:
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
}
When I run this code, the trace actually happens. I don't understand why this boolean returns true, when I just tried to remove the eventListener just above! I guess I am probably doing something really stupid because it seems like a strange error.
I hope someone can please help me on this issue.
EDIT:
I believe it has to do with the fact that the dispatchEvent function is defined inside another function when I add the listener:
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = function(event:Event):void {
dispatch(event.type, event, index);
};
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
The problem is that I need to access this "index" variable from the listener, and I can't set it as a global variable as each file has it's own index and it's a burden if I have to extend each event class to keep track of the index (Event, ProgressEvent, ..). I hope someone can please help me on this.
EDIT2:
I actually found a temporary solution, I am not sure if it is the best! I put my removeListener method actually inside the upload method, but made it a variable. As AS3 allows dynamic object, I attached this method to one of my object, and so I just call the reference to the method when necessary. The event is actually removed. Is this a good solution please?
Thank you very much,
Rudy
You're right, it has to do with the fact that you're defining a function inside another function, then using it to handle events.
Each time the function upload is called, it creates a new closure, and assigns a reference to it to the dispatchEvent variable, which is then passed to the addEventListener class. So each time upload is called, it is using a new, different closure in the call to addEventListener. Similarly, in the clearFileUploadListeners function, a new closure is being created on each call (which happens to have the same code each time, but isn't the same function object). The call to removeEventListener does nothing if the given callback has not been added as an event listener for the given event, which is the case here.
To solve your problem, you need to store a reference to the closure that you pass to the addEventListener function. This way, you can get a reference to the same closure that was added when you need to remove it later in clearFileUploadListeners.
You can try something along the lines of the following code (untested):
import flash.utils.Dictionary;
var callbackRegistry:* = new Dictionary();
private function upload(file:FileReference, index:String):void {
var dispatchEvent:Function = generateFileUploadCompleteCallback();
callbackRegistry[file] = dispatchEvent;
file.addEventListener(Event.COMPLETE, dispatchEvent);
}
private function clearFileUploadListeners(file:FileReference, index:String):void {
var dispatchEvent:Function = callbackRegistry[file];
callbackRegistry[file] = null;
file.removeEventListener(Event.COMPLETE, dispatchEvent);
var bool:Boolean = file.hasEventListener(Event.COMPLETE);
if (bool)
trace("ERROR");
else
trace("YAY, ALL OK!");
}
private function generateFileUploadCompleteCallback(index:String):Function {
return function(event:Event):void {
dispatch(event.type, event, index);
};
}
Two other things to note on this subject.
If you must utilize a native Event directly then you should pretty much always make sure and use these last three optional params :
myObject.addEventListener( Event.COMPLETE, myFunction, false, 0, true );
Check Grant Skinner's post on the subject here :
http://gskinner.com/blog/archives/2006/07/as3_weakly_refe.html
And the very best practice of all is to ALWAYS (seriously always) use Robert Penner's Signals (instead of custom events) and his NativeSignals (to wrap needed native Flash events).
Five times faster than Flash's native events.
Always safe with weak references.
Any number of typed payload(s) in each Signal.
Get the SWC here :
https://github.com/robertpenner/as3-signals
Signals were designed to solve the very problem you are having.
Imagine instead of creating an array and managing that to remove all listeners if you could just call :
signalBtnClicked.removeAll();
or
signalBtnClicked.addOnce( function( e : MouseEvent ) : void { /* do stuff */ } );
Knowing that the closure you just created will immediately be dereferenced once it is called and happily go night night when the GC makes its rounds.