Я обнаружил, что мне нужно все больше и больше вручную обновлять свою страницу до объема, начиная с создания приложения в угловом формате.
Единственный способ, которым я знаю, - это звонить $apply()
из области действия моих контроллеров и директив. Проблема в том, что он выдает ошибку в консоль, которая гласит:
Ошибка: $ digest уже выполняется
Кто-нибудь знает, как избежать этой ошибки или добиться того же, но другим способом?
angularjs
angularjs-scope
angularjs-digest
Lightbulb1
источник
источник
$timeout()
ng-*
). Убедитесь, что если вы вызываете его из функции (которая вызывается через timeout / ajax / events), то она также изначально не запускается под нагрузкой.Ответы:
Вы можете проверить, если
$digest
уже выполняется, проверив$scope.$$phase
.$scope.$$phase
вернется"$digest"
или"$apply"
если$digest
или$apply
в процессе. Я полагаю, что различие между этими состояниями заключается в том, что$digest
будут обрабатываться часы текущего объема и его дочерних$apply
элементов , и будут обрабатываться наблюдатели всех областей.К слову, @ dnc253, если вы звоните
$digest
или$apply
часто звоните , возможно, вы делаете это неправильно. Я обычно нахожу, что мне нужно переварить, когда мне нужно обновить состояние области видимости в результате события DOM, запускаемого вне досягаемости Angular. Например, когда модал начальной загрузки Twitter становится скрытым. Иногда событие DOM срабатывает, когда выполняется a$digest
, иногда нет. Вот почему я использую этот чек.Я хотел бы узнать лучший способ, если кто-нибудь знает один.
Из комментариев: @anddoutoi
angular.js Anti Patterns
источник
if (!$scope.$$phase) $scope.$apply()
", github.com/angular/angular.js/wiki/Anti-PatternsИз недавней дискуссии с ребятами из Angular на эту тему: по соображениям будущего не следует использовать
$$phase
Когда нажата для «правильного» способа сделать, ответ в настоящее время
Недавно я столкнулся с этим, когда писал угловые сервисы для обёртывания API Facebook, Google и Twitter, в которых, в той или иной степени, передавались обратные вызовы.
Вот пример из службы. (Ради краткости, остальная часть службы - которая устанавливает переменные, вводит $ timeout и т. Д. - была отключена.)
Обратите внимание, что аргумент задержки для $ timeout является необязательным и будет по умолчанию равен 0, если не установлен ( $ timeout вызывает $ browser.defer, который умолчанию равен 0, если задержка не установлена )
Немного не интуитивно понятно, но это ответ парней, пишущих на Angular, так что для меня этого достаточно!
источник
$timeout
вместо нативнойsetTimeout
, почему вы не используете$window
вместо нативнойwindow
?$timeout
в этом случае заключается в том, чтобы$timeout
обеспечить правильное обновление угловой области. Если $ digest не выполняется, это приведет к запуску нового $ digest.cancel
этого. Из документов : «В результате этого обещание будет выполнено с отказом». Вы не можете разрешить решенное обещание. Ваша отмена не вызовет никаких ошибок, но она также не принесет ничего хорошего.Цикл дайджеста - это синхронный вызов. Это не даст контроль над циклом событий браузера, пока это не будет сделано. Есть несколько способов справиться с этим. Самый простой способ справиться с этим - использовать встроенный тайм-аут в $ timeout, а во-вторых, если вы используете подчеркивание или lodash (и вам следует), вызовите следующее:
или если у вас есть lodash:
Мы попробовали несколько обходных путей и ненавидели внедрять $ rootScope во все наши контроллеры, директивы и даже некоторые фабрики. Итак, $ timeout и _.defer были нашими любимыми до сих пор. Эти методы успешно сообщают angular ждать следующего цикла анимации, что гарантирует завершение текущей области видимости. $ Apply.
источник
underscore.js
. Это решение не стоит импортировать всю библиотеку подчеркивания только для того, чтобы использовать ееdefer
функцию. Я предпочитаю$timeout
решение, потому что у всех уже есть доступ$timeout
через angular, без каких-либо зависимостей от других библиотек.Многие ответы здесь содержат полезные советы, но также могут привести к путанице. Простое использование
$timeout
- не лучшее и не правильное решение. Кроме того, обязательно прочитайте это, если вас беспокоит производительность или масштабируемость.Вещи, которые вы должны знать
$$phase
является частной структурой, и для этого есть веские причины.$timeout(callback)
будет ждать, пока текущий цикл дайджеста (если есть) будет выполнен, затем выполнить обратный вызов, а затем выполнить в конце полный$apply
.$timeout(callback, delay, false)
будет делать то же самое (с необязательной задержкой перед выполнением обратного вызова), но не будет запускать$apply
(третий аргумент), который сохраняет производительность, если вы не изменили свою угловую модель ($ scope).$scope.$apply(callback)
вызывает, среди прочего,$rootScope.$digest
что означает, что он перенаправит корневую область приложения и всех его дочерних элементов, даже если вы находитесь в изолированной области.$scope.$digest()
просто синхронизирует свою модель с представлением, но не будет переваривать родительскую область видимости, что может сэкономить много производительности при работе с изолированной частью вашего HTML с изолированной областью (в основном из директивы). $ digest не принимает обратный вызов: вы выполняете код, а затем перевариваете.$scope.$evalAsync(callback)
был представлен в angularjs 1.2 и, вероятно, решит большинство ваших проблем. Пожалуйста, обратитесь к последнему абзацу, чтобы узнать больше об этом.если вы получаете
$digest already in progress error
, то ваша архитектура неверна: либо вам не нужно повторно перенаправлять вашу область, либо вы не должны отвечать за это (см. ниже).Как структурировать свой код
Когда вы получаете эту ошибку, вы пытаетесь переварить вашу область, пока она уже выполняется: поскольку вы не знаете состояние вашей области на тот момент, вы не отвечаете за ее обработку.
И если вы знаете, что делаете и работаете над изолированной небольшой директивой, будучи частью большого приложения Angular, вы можете предпочесть $ digest вместо $ apply для сохранения производительности.
Обновление с Angularjs 1.2
Новый мощный метод был добавлен к любой области $:
$evalAsync
. По сути, он выполнит свой обратный вызов в текущем цикле дайджеста, если он происходит, в противном случае новый цикл дайджеста начнет выполнять обратный вызов.Это все еще не так хорошо, как
$scope.$digest
если вы действительно знаете, что вам нужно синхронизировать только изолированную часть вашего HTML (так как новый$apply
будет запущен, если ни один не выполняется), но это лучшее решение, когда вы выполняете функцию который вы не можете знать, будет ли он выполняться синхронно или нет , например, после извлечения потенциально кэшированного ресурса: иногда это потребует асинхронного вызова к серверу, в противном случае ресурс будет извлекаться локально синхронно.В этих случаях и во всех других, где у вас был
!$scope.$$phase
, обязательно используйте$scope.$evalAsync( callback )
источник
$timeout
критикуется мимоходом. Можете ли вы дать больше причин, чтобы избежать$timeout
?Удобный маленький вспомогательный метод, чтобы сохранить этот процесс СУХИМ:
источник
scope.$apply(fn);
должно быть,scope.$apply(fn());
потому что fn () будет выполнять функцию, а не fn. Пожалуйста, помогите мне, где я не правУ меня была такая же проблема со сторонними скриптами, такими как CodeMirror, например, и Krpano, и даже использование методов safeApply, упомянутых здесь, не решило ошибку для меня.
Но что решило, так это использование службы $ timeout (не забудьте сначала ввести ее).
Таким образом, что-то вроде:
и если внутри вашего кода вы используете
возможно, потому что он находится внутри контроллера фабричной директивы или просто нуждается в какой-то привязке, тогда вы бы сделали что-то вроде:
источник
Смотрите http://docs.angularjs.org/error/$rootScope:inprog
Проблема возникает, когда у вас есть вызов,
$apply
который иногда выполняется асинхронно вне кода Angular (когда следует использовать $ apply), а иногда и синхронно внутри кода Angular (что приводит к$digest already in progress
ошибке).Это может произойти, например, когда у вас есть библиотека, которая асинхронно выбирает элементы с сервера и кэширует их. При первом запросе элемента он будет извлечен асинхронно, чтобы не блокировать выполнение кода. Однако во второй раз элемент уже находится в кэше, поэтому его можно получить синхронно.
Способ предотвратить эту ошибку - убедиться, что вызываемый код
$apply
выполняется асинхронно. Это можно сделать, запустив ваш код в вызове$timeout
с задержкой0
(по умолчанию). Однако вызов вашего кода внутри$timeout
устраняет необходимость вызова$apply
, потому что $ timeout вызовет другой$digest
цикл, который, в свою очередь, выполнит все необходимые обновления и т. Д.Решение
Короче, вместо этого:
сделай это:
Только звонок
$apply
случае, если вы знаете, что выполняемый код всегда будет выполняться за пределами кода Angular (например, ваш вызов $ apply будет происходить внутри обратного вызова, который вызывается кодом вне кода Angular).Если кто-то не осознает какой-то существенный недостаток использования
$timeout
over$apply
, я не понимаю, почему вы не всегда могли бы использовать$timeout
(с нулевой задержкой) вместо этого$apply
, поскольку это будет делать примерно то же самое.источник
$apply
сам, но все еще получаю ошибку.$apply
синхронный (его обратный вызов выполняется, затем код, следующий за $ apply), в то время как$timeout
это не так: выполняется текущий код, следующий за тайм-аутом, затем новый стек начинается с обратного вызова, как если бы вы использовалиsetTimeout
. Это может привести к графическим сбоям, если вы обновляете дважды одну и ту же модель:$timeout
дождитесь обновления представления, прежде чем обновлять его снова.Когда вы получаете эту ошибку, это в основном означает, что она уже находится в процессе обновления вашего представления. Вам действительно не нужно звонить
$apply()
внутри вашего контроллера. Если ваше представление обновляется не так, как вы ожидаете, и после вызова появляется эта ошибка$apply()
, скорее всего, это означает, что вы не обновляете модель правильно. Если вы опубликуете некоторые подробности, мы могли бы выяснить основную проблему.источник
you're not updating the the model correctly
?$scope.err_message = 'err message';
не правильное обновление?$apply()
это когда вы обновляете модель "снаружи" angular (например, из плагина jQuery). Легко попасть в ловушку вида, который выглядит неправильно, поэтому вы бросаете кучу$apply()
s повсюду, что в итоге приводит к ошибке, замеченной в OP. Когда я говорю,you're not updating the the model correctly
я просто имею в виду, что вся бизнес-логика неправильно заполняет все, что может находиться в области видимости, что приводит к тому, что представление выглядит не так, как ожидалось.Самая короткая форма сейфа
$apply
это:источник
Вы также можете использовать evalAsync. Он будет запущен через некоторое время после завершения дайджеста!
источник
Прежде всего, не исправляйте это так
Это не имеет смысла, потому что $ phase - это просто логический флаг цикла $ digest, поэтому ваш $ apply () иногда не запускается. И помните, что это плохая практика.
Вместо этого используйте
$timeout
Если вы используете подчеркивание или lodash, вы можете использовать defer ():
источник
Иногда вы все равно будете получать ошибки, если будете использовать этот способ ( https://stackoverflow.com/a/12859093/801426 ).
Попробуй это:
источник
$rootScope
иanyScope.$root
тот же парень.$rootScope.$root
избыточноВы должны использовать $ evalAsync или $ timeout в соответствии с контекстом.
Это ссылка с хорошим объяснением:
источник
попробуйте использовать
вместо
$ applyAsync Запланировать вызов $ apply для более позднего времени. Это может быть использовано для постановки в очередь нескольких выражений, которые должны оцениваться в одном и том же дайджесте.
ПРИМЕЧАНИЕ. В $ digest $ applyAsync () будет сбрасываться только в том случае, если текущая область действия - $ rootScope. Это означает, что если вы вызовете $ digest для дочерней области, она не будет неявно очищать очередь $ applyAsync ().
Exmaple:
Ссылки:
1. Scope. $ ApplyAsync () против Scope. $ EvalAsync () в AngularJS 1.3
источник
Я бы посоветовал вам использовать пользовательское событие, а не запускать цикл дайджеста.
Я пришел к выводу, что трансляция пользовательских событий и регистрация слушателей этих событий является хорошим решением для запуска действия, которое вы хотите выполнить независимо от того, находитесь ли вы в цикле дайджеста.
Создавая пользовательское событие, вы также более эффективно работаете со своим кодом, потому что вы только запускаете прослушиватели, подписанные на указанное событие, а НЕ запускаете все наблюдения, связанные с областью действия, как если бы вы вызывали область действия. $ Apply.
источник
yearofmoo проделал большую работу по созданию для нас многократно используемой функции $ safeApply:
Применение :
источник
Я смог решить эту проблему, вызывая
$eval
вместо$apply
мест, где я знаю, что$digest
функция будет работать.Согласно документации , в
$apply
основном это:В моем случае,
ng-click
изменение переменной в области видимости, и $ watch этой переменной изменяет другие переменные, которые должны быть$applied
. Этот последний шаг вызывает ошибку "дайджест уже выполняется".Заменив
$apply
на$eval
внутри выражения наблюдения переменные области видимости обновляются, как и ожидалось.Следовательно, похоже, что если дайджест будет запущен в любом случае из-за каких-то других изменений в Angular, то
$eval
это все, что вам нужно сделать.источник
использовать
$scope.$$phase || $scope.$apply();
вместоисточник
Понимание того, что угловые документы вызова проверки
$$phase
в антишаблон , я пытался получить$timeout
и_.defer
работать.Тайм- аут и отложенные методы создают вспышку непарсированного
{{myVar}}
контента в домене, как FOUT . Для меня это было неприемлемо. Мне не нужно много догматично говорить, что что-то взломано, и у меня нет подходящей альтернативы.Единственное, что работает каждый раз:
if(scope.$$phase !== '$digest'){ scope.$digest() }
,Я не понимаю опасности этого метода, или почему его описывают как взломать люди в комментариях и ангулярной команде. Команда кажется точной и легко читаемой:
В CoffeeScript это еще красивее:
scope.$digest() unless scope.$$phase is '$digest'
В чем проблема с этим? Есть ли альтернатива, которая не создаст FOUT? $ safeApply выглядит хорошо, но также использует
$$phase
метод проверки.источник
Это мой сервис утилит:
и это пример его использования:
источник
Я использовал этот метод, и он, кажется, работает отлично. Это просто ждет времени, когда цикл закончится, а затем запускается
apply()
. Просто вызовите функциюapply(<your scope>)
из любой точки мира.источник
Когда я отключил отладчик, ошибка больше не происходит. В моем случае это произошло из-за того, что отладчик остановил выполнение кода.
источник
похоже на ответы выше, но это сработало для меня ... в сервис добавить:
источник
Ты можешь использовать
чтобы предотвратить ошибку.
источник
Проблема в основном наступает, когда мы просим angular запустить цикл дайджеста, даже если он находится в процессе, что создает проблему в angular to понимание. исключение следствия в консоли.
1. Не имеет смысла вызывать scope. $ Apply () внутри функции $ timeout, потому что внутренне это делает то же самое.
2. Код идет с ванильной функцией JavaScript, потому что определен ее собственный не угловой угол, т.е. setTimeout.
3. Для этого вы можете использовать
if (!
Scope . $$ phase) { scope. $ EvalAsync (function () {
}); }
источник
Вот хорошее решение, чтобы избежать этой ошибки и избежать $ apply
Вы можете комбинировать это с debounce (0), если вызов основан на внешнем событии. Выше «debounce», который мы используем, и полный пример кода
и сам код для прослушивания какого-либо события и вызова $ digest только в нужной вам области видимости
источник
Нашел это: https://coderwall.com/p/ngisma, где Натан Уокер (в нижней части страницы) предлагает декоратор в $ rootScope для создания func 'safeApply', код:
источник
Это решит вашу проблему:
источник