Actionscript 3 AS3从事件回调报告错误

Actionscript 3 AS3从事件回调报告错误,actionscript-3,debugging,actionscript,asynchronous,event-handling,Actionscript 3,Debugging,Actionscript,Asynchronous,Event Handling,我一直在尝试在AS3的异步代码中发生错误时提供更好的调试信息 作为默认错误报告不佳的一个示例,以我在计时器回调中强制使用空指针为例,我在控制台上得到以下stacktrace: TypeError: Error #1009: Cannot access a property or method of a null object reference. at Function/<anonymous>()[/[path-to-source-file]/TestClass.as:14]

我一直在尝试在AS3的异步代码中发生错误时提供更好的调试信息

作为默认错误报告不佳的一个示例,以我在计时器回调中强制使用空指针为例,我在控制台上得到以下stacktrace:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
    at Function/<anonymous>()[/[path-to-source-file]/TestClass.as:14]
    at flash.utils::Timer/_timerDispatch()
    at flash.utils::Timer/tick()
这很少告诉我计时器回调实际上是如何链接到我的代码的

问题是:我如何获得关于创建回调的代码的信息


我在下面添加了我的一个解决方案。我很想看看这是否可以改进。

一个可能的解决方案是在使用者线程/作用域中预先创建一个错误对象

        var errorInConsumerScope:Error = new Error()

        var timer:Timer = new Timer(1000, 1)
        timer.addEventListener(TimerEvent.TIMER, internalCallback)
        timer.start()

        function internalCallback(e:TimerEvent):void 
        {
            try
            {
                // do something that could cause an error
            }
            catch (e:Error)
            {
                errorInConsumerScope.message = e.message
                throw errorInConsumerScope
            }
        }
现在我可以将stacktrace返回到调用代码中:

Error: Error #1009: Cannot access a property or method of a null object reference.
    at TestClass()[/[path-to-source-file]/TestClass.as:10]
    at Main()[/[path-to-source-file]/Main.as:9]

完整的要点是

一种可能的解决方案是在使用者线程/作用域中预先创建一个错误对象

        var errorInConsumerScope:Error = new Error()

        var timer:Timer = new Timer(1000, 1)
        timer.addEventListener(TimerEvent.TIMER, internalCallback)
        timer.start()

        function internalCallback(e:TimerEvent):void 
        {
            try
            {
                // do something that could cause an error
            }
            catch (e:Error)
            {
                errorInConsumerScope.message = e.message
                throw errorInConsumerScope
            }
        }
现在我可以将stacktrace返回到调用代码中:

Error: Error #1009: Cannot access a property or method of a null object reference.
    at TestClass()[/[path-to-source-file]/TestClass.as:10]
    at Main()[/[path-to-source-file]/Main.as:9]
完整的要点是有趣的事情

:

将错误的调用堆栈以字符串形式返回到调用时 错误构造

据此,stacktrace按预期工作。错误是在事件处理程序中构造的,该事件处理程序由timer tick事件调用

在第二个示例中,您创建了要在TestClass的构造函数中调度的错误。因此,stacktrace将显示到TestClass构造函数的链。

有趣的事情

:

将错误的调用堆栈以字符串形式返回到调用时 错误构造

据此,stacktrace按预期工作。错误是在事件处理程序中构造的,该事件处理程序由timer tick事件调用


在第二个示例中,您创建了要在TestClass的构造函数中调度的错误。因此,stacktrace将显示到TestClass构造函数的链。

您可能需要一个通用解决方案。我将使用断点和对象检查进行单元测试和适当的调试。但这里有另一个想法:

测试类:

package {
    import flash.events.TimerEvent;
    import flash.utils.Timer;

    public class TestClass {
        public function TestClass(userCallback : Function, fail : Function) {
            var timer : Timer = new Timer(1000, 1);
            timer.addEventListener(TimerEvent.TIMER, internalCallback);
            timer.start();

            function internalCallback(e : TimerEvent):void {
                try {
                    var nullProperty : String;
                    nullProperty.length;
                } catch (e:Error) {
                    fail();
                    return;
                }
                userCallback();
            }
        }
    }
}
主要内容:

输出:

Exception fault: Error: Something went wrong.
    at Function/<anonymous>()[Main.as:8]
    at Function/TestClass/$construct/internalCallback()[TestClass.as:16]
    at flash.utils::Timer/_timerDispatch()
    at flash.utils::Timer/tick()

你可能会要求一个通用的解决方案。我将使用断点和对象检查进行单元测试和适当的调试。但这里有另一个想法:

测试类:

package {
    import flash.events.TimerEvent;
    import flash.utils.Timer;

    public class TestClass {
        public function TestClass(userCallback : Function, fail : Function) {
            var timer : Timer = new Timer(1000, 1);
            timer.addEventListener(TimerEvent.TIMER, internalCallback);
            timer.start();

            function internalCallback(e : TimerEvent):void {
                try {
                    var nullProperty : String;
                    nullProperty.length;
                } catch (e:Error) {
                    fail();
                    return;
                }
                userCallback();
            }
        }
    }
}
主要内容:

输出:

Exception fault: Error: Something went wrong.
    at Function/<anonymous>()[Main.as:8]
    at Function/TestClass/$construct/internalCallback()[TestClass.as:16]
    at flash.utils::Timer/_timerDispatch()
    at flash.utils::Timer/tick()

这就是解决方案的要点,所以如果错误是在正确的时候产生的,那么它们是非常方便的事情;您可以从多个位置注册相同的侦听器函数。当我正确理解您的操作时,您的解决方案将为每个侦听器注册创建一个空错误。空对象实际上不是我们想要的。我所看到的最干净的解决方案是对不同的客户端使用不同的回调,这样Flash错误就可以很容易地与侦听器注册上下文相匹配。很容易失去追溯到创建者的能力。您所需要做的就是从代码中获得一个级别的间接寻址,如示例中所示,并且您不知道创建调用来自何处。@Brian我不明白为什么在抛出大量默认错误时需要生成新错误。在你的问题中的例子中,它甚至显示了行号。堆栈跟踪也在做它的工作。但是,在您的回答中,抛出的错误并没有告诉我错误真正在哪一行。也许只是我,我已经看到了错误堆栈跟踪的方法。@在现实世界中,问题并不总是像空指针那样简单,问题可能是由TestClass的创建者提供的参数引起的。在这种情况下,查看是谁创建了TestClass比flash player子系统的哪个部分称为Callback更有价值,这是解决方案的重点,因此如果错误是在正确的时间创建的,那么它们是非常方便的事情;您可以从多个位置注册相同的侦听器函数。当我正确理解您的操作时,您的解决方案将为每个侦听器注册创建一个空错误。空对象实际上不是我们想要的。我所看到的最干净的解决方案是对不同的客户端使用不同的回调,这样Flash错误就可以很容易地与侦听器注册上下文相匹配。很容易失去追溯到创建者的能力。您所需要做的就是从代码中获得一个级别的间接寻址,如示例中所示,并且您不知道创建调用来自何处。@Brian我不明白为什么在抛出大量默认错误时需要生成新错误。在你的问题中的例子中,它甚至显示了行号。堆栈跟踪也在做它的工作。但是,在您的回答中,抛出的错误并没有告诉我错误真正在哪一行。也许只是我,我已经看到了错误堆栈跟踪的方法。@在现实世界中,问题并不总是像空指针那样简单,问题可能是由TestClass的创建者提供的参数引起的。在那c
ase与flash player子系统的哪个部分调用Callback相比,查看是谁创建了TestClass更有价值。感谢您的回答,但是这个解决方案仍然只是从计时器/滴答声函数的角度讲述了这个故事。在错误源超出范围后很长时间内,可能会从浏览器/flash播放器调用Tick。我感兴趣的是从Main开始作用域链/堆栈跟踪的解决方案。fail方法是一个闭包,只要有引用,它就会维护上下文。您可以删除Main的实例,但它不会被垃圾收集,因为闭包上有一个引用需要Main的上下文。你能检查一下吗?clojure不是问题所在,clojure确实拥有作用域等,但变量作用域不是问题所在。我的问题与发生错误时给出的堆栈跟踪有关。在异步代码中,堆栈跟踪从Timer/tick开始,但这并没有说明是谁创建了实际的类或函数。我感兴趣的是一个解决方案,它可以从主类的角度显示谁实际创建了计时器。fail方法在Main中创建,但从浏览器事件队列线程调用。因此,当它抛出错误时,堆栈跟踪将从浏览器的角度显示。简言之:大多数时候,我对浏览器的角度不感兴趣,我想知道:我的代码的哪一部分创建了失败的东西。谢谢你的回答,但是这个解决方案仍然只是从计时器/滴答声功能的角度讲述了这个故事。在错误源超出范围后很长时间内,可能会从浏览器/flash播放器调用Tick。我感兴趣的是从Main开始作用域链/堆栈跟踪的解决方案。fail方法是一个闭包,只要有引用,它就会维护上下文。您可以删除Main的实例,但它不会被垃圾收集,因为闭包上有一个引用需要Main的上下文。你能检查一下吗?clojure不是问题所在,clojure确实拥有作用域等,但变量作用域不是问题所在。我的问题与发生错误时给出的堆栈跟踪有关。在异步代码中,堆栈跟踪从Timer/tick开始,但这并没有说明是谁创建了实际的类或函数。我感兴趣的是一个解决方案,它可以从主类的角度显示谁实际创建了计时器。fail方法在Main中创建,但从浏览器事件队列线程调用。因此,当它抛出错误时,堆栈跟踪将从浏览器的角度显示。简言之:大多数时候,我对浏览器的角度不感兴趣,我想知道:我的代码的哪一部分创建了失败的东西。