如何识别Angular2中的触发轮检测检测(调试ngDoCheck无限循环)?

时间:2017-03-31 20:22:38

标签: angular angular2-changedetection

我还没有能够进行最小化的再现,但是在我的应用程序中,我有一个无限循环,其中根组件中的ngDoCheck被无限调用,所以我&#39 ; d希望能够识别实际上正在改变的内容。

我已经查看了堆栈跟踪中的每个调用,在ngDoCheck中放置了一个断点,但找不到任何有用的东西。

我知道在更改检测中从触发更改检测会导致这种无限循环(虽然我认为我启用的调试模式会捕获它)但我无法理解手动查找任何此类实例。

是否有任何人可以记录或检查哪些内容可以深入了解引起一轮变更检测的内容?

可能不是很有用,但是在这个循环中有堆栈和事物状态的快照:

ngDoCheck infinite loop

1 个答案:

答案 0 :(得分:4)

可能会有点晚,但是对于某人可能仍然有所帮助。

我找到了几种解决方案,可以帮助您找到真正导致CD循环的原因。

第一个,正如评论中提到的那样,是检查Task的source字段,最简单的方法是

 // put this in any component which is rendered on the page
  public ngDoCheck() {
    console.log('doCheck', Zone.currentTask.source);
  }

这将输出如下内容:

enter image description here

这已经是事情,您有进一步研究的方向,但是仍然不够完美。

要找到定义操作处理程序的真实位置,我们需要区域的long-stack-trace规范。

// add this, for example, in your polyfills file 
import 'zone.js/dist/long-stack-trace-zone';

现在除了任务源之外,我们还可以检查由长堆栈跟踪准备的数据

  public ngDoCheck() {
    console.log('doCheck', Zone.currentTask.source, Zone.currentTask.data.__creationTrace_);
  }

这将输出一个数组:

enter image description here

我们对数组中每个条目的error属性感兴趣。 从0元素开始在控制台中进行调查,然后再进一步。

在此属性内将是这样的堆栈跟踪:

enter image description here

也许这种方式过于复杂,但这是我通过阅读Zone的源代码发现的。 如果有人提出更简单的解决方案,我将非常高兴。

更新1

我创建了一个小片段,可以帮助您提取较长的堆栈痕迹

function renderLongStackTrace(): string {
  const frames = (Zone.currentTask.data as any).__creationTrace__;
  const NEWLINE = '\n';

  // edit this array if you want to ignore or unignore something
  const FILTER_REGEXP: RegExp[] = [
    /checkAndUpdateView/,
    /callViewAction/,
    /execEmbeddedViewsAction/,
    /execComponentViewsAction/,
    /callWithDebugContext/,
    /debugCheckDirectivesFn/,
    /Zone/,
    /checkAndUpdateNode/,
    /debugCheckAndUpdateNode/,
    /onScheduleTask/,
    /onInvoke/,
    /updateDirectives/,
    /@angular/,
    /Observable\._trySubscribe/,
    /Observable.subscribe/,
    /SafeSubscriber/,
    /Subscriber.js.Subscriber/,
    /checkAndUpdateDirectiveInline/,
    /drainMicroTaskQueue/,
    /getStacktraceWithUncaughtError/,
    /LongStackTrace/,
    /Observable._zoneSubscribe/,
  ];

  if (!frames) {
    return 'no frames';
  }

  const filterFrames = (stack: string) => {
    return stack
      .split(NEWLINE)
      .filter((frame) => !FILTER_REGEXP.some((reg) => reg.test(frame)))
      .join(NEWLINE);
  };

  return frames
    .filter(frame => frame.error.stack)
    .map((frame) => filterFrames(frame.error.stack))
    .join(NEWLINE);
}

用法:

  public ngDoCheck() {
    console.log(renderLongStackTrace());
  }

更新2

我发现对于EventTask,区域的long-stack-trace会创建错误的堆栈跟踪,您可以在https://github.com/angular/zone.js/issues/1195

处跟踪此问题。

更新3

值得在应用程序引导之前设置Error.stackTraceLimit = Infinity;(不适用于生产),因为带有角度+区域的堆栈跟踪可能确实很有效。