【问题标题】:AS3 Error reporting from event callback来自事件回调的 AS3 错误报告
【发布时间】:2011-08-17 09:14:12
【问题描述】:

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

作为默认错误报告的示例,以我在 Timer 回调 (github gist) 中强制使用空指针的情况为例,我在控制台上得到以下堆栈跟踪:

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()

这很少告诉我计时器回调如何实际链接到我的代码。

问题是:如何获取有关创建回调的代码的信息?

我在下面添加了我的解决方案之一。我有兴趣看看这是否可以改进。

【问题讨论】:

    标签: actionscript-3 debugging actionscript asynchronous event-handling


    【解决方案1】:

    一种可能的解决方案是在消费者“线程”/“范围”中预先创建一个错误对象

            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
                }
            }
    

    这现在让我将堆栈跟踪返回到我的调用代码中:

    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]
    

    完整的要点是here

    【讨论】:

      【解决方案2】:

      有趣的事情。

      The doc to Error.getStackTrace() says:

      将错误的调用堆栈作为字符串返回 错误的构造

      据此,堆栈跟踪按预期工作。错误是在由计时器滴答事件调用的事件处理程序中构造的。

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

      【讨论】:

      • 这就是解决方案的重点,所以如果错误是在正确的时刻创建的,那么它们是非常方便的;)
      • 您从多个位置注册相同的侦听器函数。当我正确理解您将要做什么时,您的解决方案将为每个侦听器注册创建一个(空)错误。空对象实际上并不是我们想要的。我看到的最干净的解决方案是为不同的客户端使用不同的回调,以便可以轻松地将 Flash 错误匹配到侦听器注册上下文。
      • 极容易失去追溯创作者的能力。您需要做的就是从您的代码中获得一层间接性(如示例中所示),并且您对创建调用的来源一无所知。
      • @Brian 我不明白为什么在抛出的默认错误很多时需要生成新错误。在您在问题中发布的示例中,它甚至显示了行号。堆栈跟踪也在做它的工作。但是,在您的回答中,抛出的错误并没有告诉我错误真正在哪一行。也许只有我,我已经看到了错误堆栈跟踪的方法。
      • @The_asMan 在现实世界的情况下,问题并不总是像空指针那么简单,问题可能是由 TestClass 的创建者提供的参数引起的。在这种情况下,查看谁创建了 TestClass 比查看 Flash 播放器子系统的哪个部分调用回调更有价值
      【解决方案3】:

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

      测试类:

      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();
                  }
              }
          }
      }
      

      主要:

      package {
          import flash.display.Sprite;
      
          public class Main extends Sprite {
              public function Main() {
                  new TestClass(
                      function() : void { trace('Call me back when your done.'); },
                      function() : void { throw new Error('Something went wrong.'); }
                  );
              }
          }
      }
      

      输出:

      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()
      

      【讨论】:

      • 感谢您的回答,但此解决方案仍然仅从 Timer/tick() 函数的角度讲述故事。 Tick 可能在错误源超出范围后很长时间从浏览器/flash 播放器调用。我感兴趣的是在 Main 启动范围链/堆栈跟踪的解决方案。
      • fail() 方法是一个闭包,只要有引用就保持它的上下文。您可以删除Main 的实例,但它不会被垃圾回收,因为在需要Main 上下文的闭包上存在引用。你能检查一下吗?
      • clojure 不是问题,clojure 确实拥有范围等。但变量范围不是问题。我的问题与发生错误时给出的堆栈跟踪有关。在异步代码中,堆栈跟踪从 Timer/tick() 开始,但这并没有说明谁创建了实际的类或函数。我对一个解决方案感兴趣,该解决方案向我展示了从 Main 类的角度实际创建 Timer 的人。 fail() 方法在 Main 中创建,但从浏览器事件队列线程中调用。因此,当它抛出错误时,将从浏览器的角度显示堆栈跟踪。
      • 简而言之:大多数时候我对浏览器的观点不感兴趣,我想知道:我的代码的哪一部分创建了失败的东西。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多