调试 $digest already in progress 错误

Debugging $digest already in progress error

我正在构建一个复杂的混合应用程序,并且已经在真实设备上进行了测试。有时,我会从 Angular 中收到可怕的 $digest already in progress 错误 - 特别是,它似乎是在一个较长的摘要周期之后出现的。从堆栈跟踪来看,它似乎是从更新 location.href 的 Angular 延迟函数启动的,然后触发 fastclick 发送一个 touchend,进而触发导致错误的第二个摘要。有没有人遇到过同样的错误 - 如果遇到过,您是如何解决的?

对于那些感兴趣的人,这是我在堆栈跟踪中看到的:

Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.3.0/$rootScope/inprog?p0=%24digest:
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:80:32
beginPhase@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:14473:31
$apply@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:14220:21
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:22523:29
eventHandler@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:3013:25
dispatchEvent@sendClick@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/fastclick/fastclick.js:295:30
onTouchEnd@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/fastclick/fastclick.js:589:18
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/fastclick/fastclick.js:105:43
url@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:5022:19
setBrowserUrlWithFallback@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:11080:21
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:11186:40
$eval@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:14123:28
$digest@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:13939:36
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:14161:33
completeOutstandingRequest@file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:4877:15
file:///private/var/mobile/Containers/Bundle/Application/4040564A-5631-4A1A-B2FD-6E53F9A574F2/test.app/www/js/3rdparty/angular/angular.js:5250:33

这是破坏者 (source link):

FastClick.prototype.sendClick = function(targetElement, event) {
    // ... some lines skipped

    clickEvent = document.createEvent('MouseEvents');
    clickEvent.forwardedTouchEvent = true;
    targetElement.dispatchEvent(clickEvent); // got you!
};

问题是人工 click 的处理程序将 立即被触发 (demo)。这通常没问题 - 但对于 Angular 则不然,因为 touchEnd 事件是在 $digest 阶段内生成的,并且 ng-click 似乎对它必须处理的事件有点乐观$apply 代码。因此 $digest ka-boom.

无论如何,这件事似乎很容易解决:只需异步事件分派!一种相当直接的方法是将 got you! 行替换为...

setTimeout(function() {
  targetElement.dispatchEvent(clickEvent);
}, 0);

至少在我们的案例中似乎可行。 )