В чем разница между $ evalAsync и $ timeout в AngularJS?

180

Я уже некоторое время использую AngularJS и обнаружил необходимость использовать $ timeout время от времени (похоже, для запуска плагина jQuery).

Недавно я попытался получить лучшее и более глубокое понимание цикла дайджеста, и я наткнулся на функцию $ evalAsync .

Кажется, что эта функция дает аналогичные результаты $timeout, только вы не даете ей задержку. Каждый раз, когда я использовал $timeoutэто, было с задержкой 0, так что теперь я задаюсь вопросом, должен ли я использовать $evalAsyncвместо этого.

Есть ли принципиальные различия между ними? Какие случаи вы бы использовали один над другим? Я хотел бы получить лучшее представление о том, когда использовать какой.

dnc253
источник

Ответы:

263

Я недавно ответил по существу на этот вопрос здесь: https://stackoverflow.com/a/17239084/215945 (Этот ответ ссылается на некоторые обмены github с Misko.)

Подвести итоги:

  • если код ставится в очередь с использованием $ evalAsync из директивы , он должен запускаться после того, как Angular манипулирует DOM, но до того, как браузер отобразит
  • если код ставится в очередь с использованием $ evalAsync из контроллера , он должен быть запущен до того, как Angular будет манипулировать DOM (и до того, как браузер отобразит) - редко вы этого хотите
  • если код ставится в очередь с использованием $ timeout , он должен запускаться после того, как Angular манипулирует DOM, и после рендеринга браузера (что в некоторых случаях может вызвать мерцание)
Марк Райкок
источник
15
Спасибо за объяснение. Хотя я не уверен, что понимаю одну вещь. Почему это имеет значение, если вы вызываете $ evalAsync из контроллера или директивы? AsyncQueue не знает, зарегистрирован ли он из контроллера или директивы, он просто ставит его в очередь в текущей области. Имеет ли это отношение к тому, когда вещи работают в контроллере против контроллера? Я просто хочу понять эту часть.
dnc253
@ dnc253, я не смотрел код Angular, поэтому не знаю ответа на ваш (хороший) вопрос. Надеюсь, кто-то еще может прокомментировать.
Марк Райкок
15
означает «из директивы» означает «из функции связывания директивы»? Или это правда о поведении при выполнении из метода link или controller директивы?
SimplGy
5
да, действительно неясно, что здесь означают «из директивы» и «из контроллера»
thorn̈
1
@MarkRajcok, не могли бы вы уточнить здесь: если код ставится в очередь с использованием $ evalAsync из директивы, он должен работать после того, как DOM был обработан Angular - должен ли он работать после того, как DOM был манипулирован этой директивой или другими директивами?
Макс Корецкий,
59

Для тех, кто строит сложные приложения, учтите, что на ваш выбор влияет производительность. Кроме того, я хотел бы дополнить ответ Марка более техническими подробностями:

  • $ timeout (обратный вызов) будет ожидать выполнения текущего цикла дайджеста (то есть углового обновления всей модели и DOM), затем он выполнит свой обратный вызов - потенциально влияющий на угловую модель - затем запустит полное $applyв корневой области $ scope и произведет повторный анализ все.

  • $ evalAsync (обратный вызов) , с другой стороны, добавит обратный вызов в текущий или следующий цикл дайджеста. Это означает, что если вы находитесь в цикле дайджеста (например, в функции, вызываемой из некоторой ng-clickдирективы), это ничего не будет ждать, код будет выполнен сразу же. Если вы находитесь в асинхронном вызове, например, a setTimeout, $applyбудет запущен новый цикл дайджеста ( ).

Таким образом, с точки зрения производительности всегда лучше вызывать $evalAsync, если только для вас не важно, чтобы представление обновлялось до выполнения вашего кода, например, если вам нужен доступ к некоторому атрибуту DOm, такому как ширина элементов и тому подобное.

Если вы хотите получить более подробную информацию о различиях между $ timeout, $ evalAsync, $ digest, $ apply, я предлагаю вам прочитать мой ответ на этот другой вопрос: https://stackoverflow.com/a/23102223/1501926

Также обязательно прочитайте документацию :

$ EvalAsync не дает никаких гарантий относительно того, когда выражение будет выполнено, только то, что:

  • он будет выполняться после функции, которая запланировала оценку (предпочтительно до отображения DOM).
  • после выполнения выражения будет выполнен как минимум один цикл $ digest.

Примечание: если эта функция вызывается вне цикла $ digest, будет запланирован новый цикл $ digest . Однако рекомендуется всегда вызывать код, который меняет модель, из вызова $ apply. Это включает в себя код, оцененный с помощью $ evalAsync.

floribon
источник
Можете ли вы объяснить, почему $ timeout необходим, если мне нужен доступ к некоторому атрибуту DOM. Скажем, если у меня есть <table width = "{{x}}"> Разве функция наблюдения ng-bind не обновляет атрибут dom в памяти, я понимаю, что у него не будет возможности перерисовать представление до завершения цикла дайджеста.
Шридхар Чидурала
2
@SridharChidurala, поскольку DOM («HTML») обновляется во время цикла дайджеста, вам нужно подождать, пока это не будет сделано, прежде чем вы сможете прочитать предложения. Однако Angular не одобряет это, вы должны читать xнепосредственно из своей области, а не из DOM, чтобы вам не пришлось ничего ждать. Кроме того, вам лучше использовать ng-stylecss, а не устаревшее widthсвойство. Если вам нужна дополнительная помощь, пожалуйста, откройте новый вопрос на StackOverflow.
Флорибон