In ActionScript3, how to catch ErrorEvent that I've dispatched? - actionscript-3

At some points in my application, I've a try-catch block such as:
This happens inside classes that are not in the display list (not Sprites, nor any kind of DisplayObject), but extend EventDispatcher.
Those classes reside in externally loaded SWF (in case that matters).
try {
... some logic that may throw Error
} catch (e:Error) {
var errorEvent:ErrorEvent = new ErrorEvent(ErrorEvent.ERROR, true);
errorEvent.text = e.getStackTrace();
dispatchEvent(errorEvent);
}
In the root class, this is what I have:
loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtErrorHandler);
loaderInfo.uncaughtErrorEvents.addEventListener(ErrorEvent.ERROR, onErrorEventHandler);
stage.addEventListener(ErrorEvent.ERROR, onErrorEventHandler);
protected function onUncaughtErrorHandler(event:UncaughtErrorEvent):void
{
var message:String;
if (event.error is Error) {
message = event.error.getStackTrace();
} else if (event.error is ErrorEvent) {
message = ErrorEvent(event.error).text;
} else {
message = event.error.toString();
}
trace(message);
}
protected function onErrorEventHandler(event:ErrorEvent):void
{
trace(event.text);
}
Neither handlers are called, but those error events bubble up and I see them in console, and as popups in debug mode, but how do I listen to them in a root class?
I do this because I don't want the error to interrupt execution of the main thread or the particular business logic.

I think what you are looking for is a listener to uncaught error events. Here is an example of adding it in your root class:
this.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onGlobalErrors);
EDIT BY LDMS
The following works in FlashPro:
loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR,function(e:Event){
trace("I Caught an Error");
});
var err:ErrorEvent = new ErrorEvent(ErrorEvent.ERROR,true);
var obj:EventDispatcher = new EventDispatcher();
obj.dispatchEvent(err);

If your class is on stage you can add event listener for ErrorEvent.ERROR to stage. Any dispatched event will go up to the stage through all its parents.
Example of code:
// some initialize
stage.addEventListener("lol", lolHandler);
// dispatching
private function button1_clickHandler(event:MouseEvent):void
{
// second parameter responsible for bubbling and MUST be true
dispatchEvent(new Event("lol", true)); // on mouseClick we dispatch custom event
}
private function lolHandler(event:Event):void
{
trace("We got it! Lol!);
}
P.S. Article in the comment is really useful. Recommend it to read.
// UPDATE
Alternative variant can be in dispatching event from some singleton. For example:
public class ErrorHandler extends EventDispatcher {
private static var _instance:ErrorHandler;
public static function get instance():ErrorHandler {
if (!_instance)
_instance = new ErrorHandler(new PrivateConstructor());
return _instance;
}
public function ErrorHandler(value:PrivateConstructor) {
addEventListener(ErrorEvent.ERROR, errorHandler);
}
private function errorHandler(event:ErrorEvent):void {
// some code
}
}
}
class PrivateConstructor {}
.... and dispatching
ErrorHandler.instance.dispatchEvent(new ErrorEvent());

Related

Is it possible to make use of a value returned from a method called by a delegate?

While experimenting with a basic coding stuff, I wondered, if a value returned by a method called by a delegate, could be used or captured. In other words, where will the return value will go ?
For example :
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener( "myClick" , myClick);
}
function myClick(e:Event):String //returning a string
{
return "What happens to this return value ???"
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener( MouseEvent.CLICK , onClick);
}
function onClick(e:Event):String //returning a string
{
dispatchEvent(new Event("myClick"));
}
}
As I know it's not possible to do, but, there are at least some ways to implement the logic similar to what you've told about.
For example, you may call a method of a dispatcher, from a listener method:
class Main extends Sprite
{
public var mc:MyMc;
function Main()
{
mc.addEventListener("myClick" , myClick);
}
function myClick(e:Event):void
{
mc.specialMethod("some string");
}
}
class MyMc extends MovieClip
{
function MyMc()
{
addEventListener(MouseEvent.CLICK , onClick);
}
function onClick(e:Event):void
{
dispatchEvent(new Event("myClick"));
}
public function specialMethod(param:String):void
{
// Do something to the param
}
}
Also, you may think about dispatching an event from the Main class, and listen to it in the MyMc class, or pass a callback, which returns a string, from Main to the MyMc.
It's according to you and your needs to return something from the listener function because normally it must return nothing :
... This function must accept an Event object as its only parameter and must return nothing, ...
but you can of course get the returned value(s), take a look on this little example :
var light_on:Boolean = false;
btn_light_on.addEventListener(MouseEvent.CLICK, btn_on_onPress);
btn_light_off.addEventListener(MouseEvent.CLICK, btn_off_onPress);
function btn_on_onPress(e:MouseEvent): Boolean {
light_on = true;
if(e.target === btn_light_off){
light_on = false;
}
return light_on;
}
function btn_off_onPress(e:MouseEvent): void {
trace('The light is', btn_on_onPress(e) ? 'on' : 'off');
}
Hope that can help.

object's stage reference lost when re-instantiated after being removed from memory & displaylist

I'm building a game. When you die or win a level you're prompted to continue or return to the main menu. If you chose to go to the main menu you can start a new game. When you start a new game, the game is object is created again & it's children have lost their reference to the stage. I'm not sure why this is happening and I've spent over a week trying to figure out why. Here's some code (and descriptions of the code) from the game that should hopefully provide enough insight as to why the problem may be occurring:
if the "new game" button is clicked on the startMenu the NavigationEvent.START event is dispatched. a LevelEvent.COMPLETE event is dispatched by WeeBeeGame when the level is completed.
public class DocumentClass extends MovieClip {
public var startMenu:StartMenuGenerator = new StartMenuGenerator();
public var weeBeeGame:WeeBeeGame;
public var youWonBox:YouWonBox = new YouWonBox();
public function DocumentClass() {
// constructor code
addChild(startMenu);
startMenu.addEventListener(NavigationEvent.START, startGameHandler);
}
public function startGameHandler(e:NavigationEvent) : void {
this.removeChild(startMenuBG);
removeChild(startMenu);
weeBeeGame = new WeeBeeGame();
this.addChild(weeBeeGame);
weeBeeGame.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler);
}
public function levelCompleteHandler(e:LevelEvent) : void {
youWonBox.x = this.stage.stageWidth/2;
youWonBox.y = this.stage.stageHeight/2;
addChild(youWonBox);
youWonBox.addEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mouseClickHandler(e:MouseEvent) : void {
if(e.target.name === "mainmenubtn"){
mainmenuHandler();
}
}
private function continueHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
}
private function mainmenuHandler() : void {
youWonBox.removeEventListener(MouseEvent.CLICK, mouseClickHandler);
WeeBeeGame.collisionDOC.removeChildren();
removeChild(weeBeeGame);
weeBeeGame = null;
this.addChild(startMenuBG);
addChild(startMenu);
removeChild(youWonBox);
}
}
the code that dispatches a LevelEvent.COMPLETE event is not shown but it dispatches when the level is complete. collisionDOC needs to be static because it's needed in a lot of other classes and holds the display objects needed for a 3rd party pixel-level collision detection.
public class WeeBeeGame extends MovieClip {
public var bee: Bee;
public var beeHurt:BeeHurt;
public var spawningDaemon:SpawningDaemon;
public static var collisionDOC:DisplayObjectContainer;
public function WeeBeeGame() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler, false, 0, true);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
collisionDOC = new MovieClip();
addChild(collisionDOC);
bee = new Bee();
collisionDOC.addChild(bee);
beeHurt = new BeeHurt(bee.x, bee.y);
addChild(beeHurt);
beeHurt.visible = false;
spawningDaemon = new SpawningDaemon(currentLevel);
this.addEventListener(LevelEvent.COMPLETE, levelCompleteHandler, false, 0, true);
}
private function levelCompleteHandler(e:LevelEvent) : void {
removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
}
}
the first line to throw a 1009 error (Cannot access a property or method of a null object reference) is the line containing stage.mouseX because the stage reference is null.
public class Bee extends MovieClip {
public function Bee() {
this.addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
}
private function addedToStageHandler(e:Event) : void {
this.removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
this.x = this.stage.stageWidth / 2;
this.y = this.stage.stageHeight - this.stage.stageHeight / 9;
this.addEventListener(Event.ENTER_FRAME, enterFrameHandler, false, 0, true);
}
private function enterFrameHandler(e:Event) : void {
if(stage == null){
trace('stage is null' + stage.mouseX);
}
}
}
When the swf is first opened and a new WeeBeeGame is created its children have references to the stage. But when WeeBeeGame and it's children are removed from the display list and memory they lose reference and even if they're re-instantiated their references are still lost. How do I fix this? I'm very confused. Thank you all!!
ENTER_FRAME handlers continue to execute even when the display object is removed form the stage. So when you removeChild(weeBeeGame) those ENTER_FRAME handlers are still trying to access stage.mouseX each frame. You need to stop the ENTER_FRAME handlers.
Probably the easiest fix is to add an Event.REMOVED_FROM_STAGE handler to remove the Event.ENTER_FRAME handlers.
A better fix IMO is to not add any ENTER_FRAME handlers from your game objects, but rather expose a public function like update() which gets called from a single ENTER_FRAME handler in your WeeBeeGame when the game is running. Then to stop the game completely you can stop all updates by simply removing that single ENTER_FRAME handler. This is a common practice.

Best way to get a variable from another class in AS3

I wonder which one of these methods is the best when trying get a variable from anotherClass to the Main-document-class, and why? Are there any more, even better ways?
In example no 1, and the Main-function, can the if-else statement be triggered before the checkLogin function was completely done? Thanks!
My no 1. way
public class Main extends MovieClip {
// Declaring other classes.
private var checkLogin:CheckLogin;
public function Main() {
checkLogin = new CheckLogin();
if(checkLogin.isLoggedIn == true) {
trace("Is logged in");
} else {
trace("Not logged in");
}
}
}
and
public class CheckLogin {
public var isLoggedIn:Boolean;
public function CheckLogin() {
isLoggedIn = false;
}
}
Or is it a lot better to do it this way,
(Way no 2):
public class Main extends MovieClip {
// Declaring other classes.
private var checkLogin:CheckLogin;
public function Main() {
checkLogin = new CheckLogin();
checkLogin.addEventListener("checkLogin.go.ready", checkLoginReady);
checkLogin.go();
}
public function checkLoginReady(event = null) {
if(checkLogin.isLoggedIn == true) {
trace("Is logged in");
} else {
trace("Not logged in");
}
}
}
and
public class CheckLogin extends EventDispatcher {
public var isLoggedIn:Boolean;
public function CheckLogin() {
isLoggedIn = true;
}
public function go() {
this.dispatchEvent(new Event("checkLogin.go.ready"));
}
}
Generally, using events will make your code easier to maintain.
The login check you describe seems to be instant, so approach number 1 would be ok for such a simple case.
Often the login check (or whatever process) may not be instant, for example if the CheckLogin class sent a server request and waited for the response. In this case using events is better. The Main class can listen for a LOGIN_SUCCESS or LOGIN_FAIL event and handle it when the event occurs. All the login logic stays within CheckLogin.
Also, using events will allow multiple interested classes to react, without them knowing any of the inside login process.

Custom Event not working

I have created a Custom Event, that is fired from a custom component. It should be catched in the main application to change the selectedindex of the viewstack.
But this doesn't work, and I can't figure out why.
This is my Custom Event:
package events
{
import flash.events.Event;
public class ChangeSelectedIndex extends Event
{
public static var index_passed:String = "Index passed";
private var index:int;
public function ChangeSelectedIndex(i:int, type:String, bubbles:Boolean=false, cancelable:Boolean=false)
{
super(type,bubbles, cancelable);
index = i;
}
public function get getIndex():int
{
return index;
}
}
}
This is how I fire the event:
protected function checkUsernameExistsDbSucces(event:ResultEvent):void
{
dispatchEvent( new ChangeSelectedIndex(1,ChangeSelectedIndex.index_passed,false,false));
}
And this is my function to catch the event:
private function changeSelectedIndexHandler(event:ChangeSelectedIndex):void
{
mainViewStack.selectedIndex = event.getIndex;
}
In order for your handler to be called when the event is dispatched, you need to add an event listener to your custom component.
myCustomComponent.addEventListener ( ChangeSelectedIndex.index_passed, changeSelectedIndexHandler);
Make sure this line is within the same scope as both your handler function and myCustomComponent, otherwise there will be an error.

How do I handle a custom event in ActionScript 3?

I've created an Event Handler/Listener like so:
import flash.events.Event;
public class DanielEvent extends Event {
public var data:*;
public static const APP_STARTED:String = "APP_STARTED";
public function DanielEvent(n:String, data:*){
this.data = data;
super(n)
}
}
Listening to an event using:
addEventListener(DanielEvent.APP_STARTED, appStarted);
Dispatching an event by:
dispatchEvent(new DanielEvent("APP_STARTED", "test"))
And receiving the data by:
private function appStarted(e:Event){
trace(e.data)
}
But I get the error:
Access of possibly undefined property
data through a reference with static
type flash.events:Event.
You have to use your custom event type in the event handler, if you want to access the data property:
private function appStarted(e:DanielEvent): void {
trace(e.data);
}
your event handler is passed a DanielEvent, not an Event:
private function appStarted(e:DanielEvent):void
{
trace(e.data);
}
also. you should also use your constant for your dispatch instead of passing a string, like you've done for your listener:
dispatchEvent(new DanielEvent(DanielEvent.APP_STARTED, "test"));
and don't forget to override clone() if you are planning on dispatching that event more than once.
public override function clone():Event
{
return new DanielEvent(n, data);
}