Angular $scope. $apply vs $timeout как безопасный $apply
Я пытаюсь лучше понять нюансы использования службы $timeout в Angular как своего рода метод "безопасный метод $apply". В основном в сценариях, где фрагмент кода может выполняться в ответ на событие Angular или событие без angular, такое как jQuery или какое-либо стандартное событие DOM.
Как я понимаю вещи:
- Копирование кода в $scope. $apply отлично работает для сценариев, где вы
еще не находятся в цикле дайджеста (событие jQuery), но будет вызывать ошибку, если выполняется дайджест.
- Код обложки в вызове $timeout() без параметра задержки работает ли уже в цикле дайджест или нет
Посмотрев исходный код Angular, он выглядит так: $timeout делает вызов $rootScope. $apply().
- Почему не $timeout() также вызывает ошибку, если цикл дайджеста уже выполняется?
- Лучше всего использовать $scope. $apply(), если вы точно знаете, что дайджест не будет выполняться, и $timeout(), когда это необходимо для безопасности в любом случае?
- Является ли $timeout() действительно приемлемым "безопасным", или есть ли gotchas?
Спасибо за понимание.
Ответы
Ответ 1
Посмотрев исходный код Angular, он выглядит так: $timeout делает вызов $RootScope. $Применяются().
- Почему не $timeout() также вызывает ошибку, если цикл дайджеста уже выполняется?
$timeout
использует недокументированную службу Angular $browser
. В частности, он использует $browser.defer()
, который прерывает выполнение вашей функции асинхронно через window.setTimeout(fn, delay)
, который всегда будет работать за пределами Angular жизненного цикла. Только один раз window.setTimeout
запустил вашу функцию, будет $timeout
вызов $rootScope.$apply()
.
- Лучше всего использовать $scope. $apply(), если вы точно знаете, что дайджест не будет выполняться, и $timeout(), когда это необходимо для безопасности в любом случае?
Я бы так сказал. Другим вариантом использования является то, что иногда вам нужно получить доступ к переменной $scope, которая, как вы знаете, будет инициализирована после дайджеста. Простым примером может быть, если вы хотите, чтобы состояние формы загрязнялось внутри вашего конструктора контроллера (по какой-либо причине). Без $timeout FormController
не был инициализирован и опубликован в $scope, поэтому wrapping $scope.yourform.setDirty()
внутри $timeout гарантирует, что FormController
был инициализирован. Конечно, вы можете сделать все это с помощью директивы без $timeout, просто используя другой пример использования.
- Является ли $timeout() действительно приемлемым "безопасным", или есть ли gotchas?
Это всегда должно быть безопасно, но ваш подход к методу всегда должен быть нацелен на $apply(), на мой взгляд. Текущее приложение Angular, над которым я работаю, довольно велико, и нам нужно было полагаться только на $timeout, а не $apply().
Ответ 2
Если мы используем $apply в приложении, мы можем получить уже выполненный Error: $digest. Это происходит потому, что один цикл $digest может запускаться одновременно. Мы можем разрешить его с помощью $timeout или $evalAsync.
Тайм-аут $не генерирует ошибку, как "$ digest уже выполняется", потому что $timeout сообщает Angular, что после текущего цикла ожидания ожидания ожидания ожидания и таким образом гарантируют, что не произойдет никаких столкновений между циклами дайджеста и, следовательно, выход $timeout будет выполняться в новом цикле $digest.
Я попытался их объяснить: Сравнение применения, таймаута, дайджеста и evalAsync.
Может быть, это поможет вам.
Ответ 3
Насколько я понимаю, $timeout
представляет собой оболочку вокруг setTimeout
, которая неявно вызывает $scope.$apply
, что означает, что она выполняется за пределами жизненного цикла angular, но запускает сам жизненный цикл angular. Единственное, о чем я могу подумать, это то, что если вы ожидаете, что ваш результат будет доступен для этого $digest
, вам нужно найти другой способ "безопасного применения" (который, AFAIK, доступен только через $scope.$$phase
).