I'm debating two approaches to a pretty typical problem: Knowing when an event occurs or responding to it immediately if it already HAS occurred.
In approach one, a user of MyLoader1 adds an event listener which will be fired immediately if the loader is already complete.
class MyLoader1 extends EventDispatcher
{
private var _isComplete:Boolean = false;
public override function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
{
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
// if the operation is already complete, immediately notify listeners
if(_isComplete)
{
dispatchEvent(new Event(Event.COMPLETE));
}
}
}
class Application1()
{
function main():void
{
new MyLoader1().addEventListener(Event.COMPLETE, onComplete);
}
}
In approach 2, a user of MyLoader2 must first check the completion status of MyLoader2 before deciding whether to proceed or add a listener, which is fired later.
class MyLoader2 extends EventDispatcher
{
private var _isComplete:Boolean = false;
public function get isComplete():void
{
return _isComplete;
}
}
class Application2()
{
function main():void
{
var loader:MyLoader2 = new MyLoader2();
if(loader.isComplete)
{
// passing null just to simplify the example
onComplete(null);
}
else
{
loader.addEventListener(Event.COMPLETE, onComplete);
}
}
}
What advantages/disadvantages are there to each approach? Is there a design pattern out there that I could/should be using for this situation?
I'm leaning towards the first because it requires less knowledge of the Loader class and less code to leverage it. It could potentially create unwanted side effects when there are multiple listeners though, because the event will fire once for each time a listener is added.
The second method is understandable and easier to debug, but requires more up front work and seems to break encapsulation of the Loader.
I like your first approach better. I don't think that dispatching one event for each listener added is a problem, though; in fact, that's the very idea behind the event mechanism. If you have N objects that want to be notified whenever FooEvent occurs, you have to dispatch the event for each one whenever this event takes place.
Having said that, I wouldn't dispatch the event in the addEventListener method; I think that's the unwanted side effect, really. It goes against anyone's reasonable expectations. Adding a listener should not cause the event to fire. It should just register a listener. You should check whether the data is already loaded in your load function and dispatch the event there if the data is available (because at that point your load operation completed; not when you added the listener).
Another thing: I understand that you want to dispatch immediately if possible. But this has a problem, that can be serious and lead to annoying and not so obvious bugs. If you dispatch immediately you basically have 2 interfaces: one asynchronous and one synchronous. It's possible to handle this correctly in the calling code, but it requires more work and it's quite error prone, especially if you have chained events somewhere (I've made the error of having this kind of async/sync loader and I learned this the hard way).
It's much simpler and it makes almost no difference to delay the dispatching of the event in case the data is available right away. Just a tiny delay to make the code that handles the event run asynchronously (setTimeout(dispatchComplete,10) will do it), in a different stack frame that the code that called the loader. You'll save yourself some troubles and make your calling code simpler, which I think is what you're after.
Though slightly off topic, I would suggest you give signals a try. It depends on what kind of events you are using (ie. Mouse Events would still require the as3 Event so for some instances it might be a bit of extra work), but I've found signals a lot cleaner to implement, and for custom events, it is my preferred choice.
using a signals I usually set up one static var that acts as the main controller. I find this is better than the interconnected chain of Event Listeners and Dispatchers. You could have all the commands driving your app/game/website going through this one funnel.
The reason I'm bringing this up is that if you go this route, you essentially have a listener before you have the event. So if an object is created after the event has taken place, you could have it poll for whether an event occoured, and the addOnce() function is good for loaders and other events that are expected to happen once only. So while this does not answer your question, I hope it adds to the confusion :)
there's a link here to give you an idea of how it works
http://johnlindquist.com/2010/01/21/as3-signals-tutorial/
Related
Usually when dispatching an Event (or custom Event) I use this:
function fireEvent():void {
dispatchEvent(new Event(Event.COMPLETE));
}
Is better use a single object to dispatch an event every time? Can I do this?
var myEvent:Event = new Event(Event.COMPLETE);
function fireEvent():void {
dispatchEvent(myEvent);
}
It would be a way better (eg create event pools) if it works. Unfortunately AS3 does some internal magic with event object during event flow which makes objects only one time usable (Sorry, by some reason I can't find proof quickly).
You can easily get confirmation by dispatching event second time, your listeners will not be triggered (may be it's true only for bubbling feature, not remember exactly), so you have to create a new instance of event object for every dispatchEvent call.
It doesn't worth it even if pooling works, I presume. Creating event objects isn't expensive in astionscript, adding event listeners is. You'd better put this effort on managing listeners and handlers which could easily become the performance buttleneck.
first I'd like to thank you for taking the time to read my question.
So, I've made a little flash game(can't really call it a game), anyway at one point I'm checking for if 2 objects hit each other. So to sum it up here's the code
public function loop(e:Event):void
{
y += vx;
if(y > stageRef.stageHeight || y < 0)
removeSelf();
if(hitTestObject(target.hit))
{
removeSelf();
stageRef.dispatchEvent(new GameOverEvent(GameOverEvent.DEAD)); // <--
stageRef.addChild(new SmallImplosion(stageRef, x, y));
}
}
So, when they collide, one object dispatches an event, there is no problem with the code, it works, but I would like to know if it's good to make handle it this way. stageRef is the reference to stage, both classes have it.
And my other class catches that event and it triggers a function, like this:
stageRef.addEventListener(GameOverEvent.DEAD, takeHit, false, 0, true);
The question is, is this a good way to handle it? Thank you in advance!
When implementing the observer pattern, each of the participating objects has a clear role: One is the subject, who is the object where the action is happening, while the other is the observer, who listenes for events on the subjects. In Flash, any object can be the observer, as you pass the handling function to the subscribe-method. Subjects however implement IEventDispatcher and provide the necessary methods to subscribe to the subject. There is also the standard implementation EventDispatcher which already implements the necessary methods (and many types are subtypes of it).
Now back to your question; you are essentially bringing in a third party, the stage where the events are broadcasted on. Instead of having the events that are local to the subject dispatched on the subject itself, you are dispatching them over the global stage, and all observers have to listen to the stage instead of the subject itself.
This is generally not what you should do. Every subject (IEventDispatcher) should only dispatch its own events. Just like you receive a click event from the button that is clicked, you’d receive a GameOverEvent from the object that triggers it.
From the name GameOverEvent, I believe a bit of significance is implied. Since there's plenty to do at the end of a game, I feel it's appropriate for your stage to listen for an event such as this.
However, don't use this technique to handle any smaller systems. For example, if the collision only dealt some damage to the player (I don't know if this even makes sense in the context of your game but roll with me here) then it would be better to have the classes that check for collisions and the classes that handle health to interact directly with one another instead of firing these "Grand Events".
Is flash.utils.Timer object's event TIMER always dispatched before TIMER_COMPLETE?
During the 2nd event, I am nullifying stuff that are required during the 1st event; so their order is of prime importance. I checked the docs and there is no guarantee for their dispatching order.
In tests I've done it seems that this is the case, but I don't want to distribute publicly software without confirming first.
You can avoid this problem by using TimerEvent.TIMER only:
private function onTimer(event:TimerEvent)
{
// ...
if (timer.currentCount == timer.repeatCount) {
// timer is complete
}
}
I'm almost positive this is the case, since the code seems to be in the player itself I don't think you can get at the source to get a legitimate confirmation, however I have always seen this to be the case myself and from how the docs read it sounds as though a TIMER event would always be dispatched before the complete event
timerComplete
Dispatched whenever it has completed the number of requests set by Timer.repeatCount.
timer
Dispatched whenever a Timer object reaches an interval specified according to the Timer.delay property.
So I imagine the timerComplete is dispatched after it receives enough timer events that the currentCount equals the repeat count then a timerComplete is dispatched, however without being able to look at the code it's impossible for anyone to completely confirm this. Possibly you could look at the Gnash source to see how it's handled by that implementation of the player, but it's not necessarily the same in the normal Flash Player.
When I add an EVENT.ACTIVATE listener to my project, and then alt-tab away and back to my project it triggers twice.
edit: shaunhusain and I seem to have found the cause of the problem, although without a solution. When running the standalone player version 11+ the event triggers 2x. When running standalone player version <11 or any version in the browser it triggers 1x. So it appears there may be a bug in recent versions of the flash player projector. I'm going to nail down the exact versions and report it to adobe and see what happens. Thanks to anyone who read this and tried to help!!
I want it to fire every time I change focus, I just don't want it to fire twice every time I change focus.
Why is this? Am I doing something wrong? What's the best way to prevent this behavior?
It seems like it would be a common question, but Google turned up nothing.
Code:
package
{
import flash.display.Sprite;
import flash.events.Event;
public class Main extends Sprite
{
public function Main():void
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
// entry point
stage.addEventListener(Event.ACTIVATE, test);
}
private function test(e:Event):void
{
trace(e.target);
}
}
}
Actual result:
[object Stage]
[object Stage]
Desired result:
[object Stage]
It doesn't seem to make a difference whether I add the listener to the stage or anything else, the behavior is the same.
The same thing also happens with EVENT.DEACTIVATE. Others such as mouse up work fine.
My goal is to pause a game on EVENT.DEACTIVATE and unpause it on EVENT.ACTIVATE. The problem is that when the event fires twice, it calls the unpause function twice which has unwanted consequences.
ActionScript® 3.0 Reference for the Adobe® Flash® Platform says about this event:
Dispatched when the Flash Player or AIR application gains operating
system focus and becomes active. This event is a broadcast event,
which means that it is dispatched by all EventDispatcher objects with
a listener registered for this event. For more information about
broadcast events, see the DisplayObject class.
For me it looks like you want to prevent its designed behavior? I suppose it was designed to fire every time you change focus, or am I wrong? What do you want to accomplish? However this is an answer, so based on what you wrote - you can do a workaround by just removing a listener after he fired once:
private function test(e:Event):void
{
stage.removeEventListener(Event.ACTIVATE, test);
trace(e.target);
}
But I would recommend you to write something more about why are you using it and what want to accomplish if this is not satisfactory.
I've had the same issue in my AIR Mobile app.
To correct this issue, I've stored the last event name triggered for an Activate / Deactivate event. If it is attempted twice in a row, it just gets skipped with a return;
private static function onAppStateChanged(e:Event):void {
if (_STATE == e.type) {
return;
}
_STATE = e.type;
switch(_STATE) {
case Event.ACTIVATE: whenActivated.dispatch(); break;
case Event.DEACTIVATE: whenDeactivated.dispatch(); break;
}
}
At first, the value of _STATE begins with null, so that should allow it to pass through the first time around.
I bind both Event.ACTIVATE and Event.DEACTIVATE to the same listener, onAppStateChanged in my App's initialization method, like so:
_STAGE.addEventListener(Event.ACTIVATE, onAppStateChanged);
_STAGE.addEventListener(Event.DEACTIVATE, onAppStateChanged);
You can replace the Switch-Statement however you like, I just personally prefer using Robert Penner's AS3Signals (GitHub) to broadcast the resume/suspend signal across the App.
My problem is basically as follows. There is an instance of a class extending EventDispatcher. Everything goes well when I add event listener to the object like this:
myObject.addEventListener('eventName', someFunction, false, 0, false);
But something changes when event listener is added by a weak reference:
myObject.addEventListener('eventName', someFunction, false, 0, true);
Now someFunction is not called even though the line containing dispatchEvent('eventName') is being executed just like before (and there is an external reference to myObject as well).
The application I’m developing is quite complex so, unfortunately, I can’t post the exact code.
You are misunderstaning how GC works, I think. Using a weak reference will not pin down myObject. It could (possibly, not necessarily) prevent the scope in which the handler is declared to be collected (as long as myObject itself is alive). Hence, the callback itself could be collected, causing it not to be executed. It seems this is the case here, according to your description.
So your goal is to avoid that the scope declaring someFunction is collected. One way could be actually using a hard ref (if you ask me, weak refs are a bad idea, anyway). There may be others, but I don't know how your app is structured, so I can't say much.
Edit to address a comment.
GC wise, event handling is not different from the general case. When you add a listener, you're passing a reference to the dispatcher (the reference is the scope that in which the listener is declared). The dispatches stores this reference so it can execute the callback when neccesary. It's really not that different from this:
myObject.keepThisReference = someFunction;
Now, myObejct has a reference to someFunction. So, as long as myObject is alive, someFunction will be alive too (unless you set someFunction as a weak ref).
So, to answer the question in your comment (an object is not referenced anywhere else but has an event listener attached), given this scenario:
myObject.addEventListener('someEvent',someFunction);
If myObject doesn't have any other reference and goes out of scope, it is collectable.
If there's no other link to myObject, then it'll get collected when the function that you're in goes out of scope. Either change the code so it's not a weak listener, or make sure there's a reference to myObject somewhere else in the code