调试$ apply已在进行中

时间:2014-03-27 08:59:30

标签: angularjs google-chrome-extension

我正在使用AngularJS开发Chrome扩展程序(因此它在CSP模式下运行)。 amServices.js包含处理Chrome本机消息传递的服务。所以在js / core / am / amServices.js:268:20相关代码如下:

chrome.runtime.onMessage.addListener(
  function (message, sender, sendResponse) {
    $rootScope.$apply(function () {
      if (message.type == 'login' && message.state == 'ok') {
//huge if/else if here for every kind of message

我的理解是,由于这里的所有代码都是异步调用并且可以在大多数应用程序视图中触发修改,因此$ rootScope。$ apply是必需的。然而,在一些看似完全随机的方式中,我有时会在控制台中获得这些:

Error: [$rootScope:inprog] http://errors.angularjs.org/1.2.13/$rootScope/inprog?p0=%24apply
    at Error (native)
    at chrome-extension://hbfchfkepmidgcdfcpnodlnmfjhekcom/lib/angular/angular.min.js:6:450
    at n (chrome-extension://hbfchfkepmidgcdfcpnodlnmfjhekcom/lib/angular/angular.min.js:98:34)
    at h.$apply (chrome-extension://hbfchfkepmidgcdfcpnodlnmfjhekcom/lib/angular/angular.min.js:104:195)
    at chrome-extension://hbfchfkepmidgcdfcpnodlnmfjhekcom/js/core/am/amServices.js:268:20
    at Function.target.(anonymous function) (extensions::SafeBuiltins:19:14)
    at Event.dispatchToListener (extensions::event_bindings:394:22)
    at Event.dispatch_ (extensions::event_bindings:378:27)
    at Event.dispatch (extensions::event_bindings:400:17)
    at messageListener (extensions::messaging:192:31) 

没有什么可以让我感到困惑的是,与此处所解释的不同:

Why doesn't Angular ignore subsequent $digest invocations?

在我的堆栈跟踪中,我没有看到两个$ apply调用,所以我无法知道冲突的来源。另外,我无法运行AngularJS Batarang调试工具,因为它不适用于CSP模式。

我忽略了这些错误而没有任何明显的后果,但我不确定忽略它们是否安全。关于如何知道哪两个应用调用的任何想法都引发了冲突?

2 个答案:

答案 0 :(得分:3)

如果你的问题是为什么?并且您预计只有在您有多个$apply()

时才会出现错误

好吧,正如@ J.Wells在你的问题评论中提到的那样,可能是因为其他angular指令触发$scope.$apply并且chrome.runtime的回调发生在角内部在$$phase

查看ng-click的{​​{3}},angularjs内部使用开发人员可用的$scope.$apply

因此,只需考虑ng-clickng-change等角度指令使用$ scope。$ apply计算表达式,这将启动$apply阶段,以及ng-if等指令}和ng-hide使用$ scope。$ watch计算表达式,它位于$digest阶段。

糟糕的理论,因为正如@Wawy建议的那样,javascript是单线程的,因此在摘要中不能执行回调(左边以便注释有意义): 如果在单击“登录”按钮时发生ng-click指令,可能是因为在$digest ng-click阶段完成之前,chrome绑定并响应太快,所以onMessage回调已执行。

答案 1 :(得分:2)

要回答这个问题,我们需要看一下$ digest和$ apply的实现:

$digest: function() {
    //Simplified code

    beginPhase('$digest'); 
    /* $$phase = 'digest' 
       From now on any code that calls $apply will cause an exception.
    */

    lastDirtyWatch = null;

    do { // "while dirty" loop
      dirty = false;
      current = target;

      //Running any $evalAsync your code might have called
      while(asyncQueue.length) {
        try {
          asyncTask = asyncQueue.shift();
           // If any $evalAsync expression calls $apply will cause an exception.
          asyncTask.scope.$eval(asyncTask.expression);
        } catch (e) {
          clearPhase();
          $exceptionHandler(e);
        }
        lastDirtyWatch = null;
      }

      traverseScopesLoop:
      do { // "traverse the scopes" loop
        if ((watchers = current.$$watchers)) {
          // process our watches
          // If any $watch expression calls $apply it will cause an exception.
          ...
        }
      } while ((current = next));

    } while (dirty || asyncQueue.length);
}

$适用:

$apply: function(expr) {
        try {
          beginPhase('$apply');
          //Any code inside expr that calls $apply or any function that does it will cause an exception.
          return this.$eval(expr);
        } catch (e) {
          $exceptionHandler(e);
        } finally {
          clearPhase();
          try {
            $rootScope.$digest();
          } catch (e) {
            $exceptionHandler(e);
            throw e;
          }
        }
      },

在分析了这两种方法后,我们找到了可能导致异常的3个地方:

$ evalAsync表达式,$ watch表达式和$ apply表达式。因此,直接或间接调用$ apply的那些表达式中的任何代码都将触发异常。

值得一提的是,内部的角度也会调用$ apply,所以如果你在角度'领域'内,你就不应该调用$ apply。