Я пытаюсь понять, как работает Ember RunLoop и что заставляет его работать. Я просмотрел документацию , но у меня осталось много вопросов. Мне интересно лучше понять, как работает RunLoop, чтобы я мог выбрать подходящий метод в его пространстве имен, когда мне придется отложить выполнение некоторого кода на более позднее время.
- Когда запускается Ember RunLoop. Это зависит от маршрутизатора, представлений, контроллеров или чего-то еще?
- сколько времени это примерно займет (я знаю, что это довольно глупо спрашивать и зависит от многих вещей, но я ищу общую идею, или, может быть, есть минимальное или максимальное время, которое может занять цикл выполнения)
- Выполняется ли RunLoop постоянно или просто указывает период времени от начала до конца выполнения и может не работать в течение некоторого времени.
- Если представление создается из одного цикла RunLoop, гарантировано ли, что все его содержимое попадет в DOM к моменту завершения цикла?
Простите меня, если это очень простые вопросы, я думаю, понимание их поможет таким новичкам, как я, лучше использовать Ember.
Ответы:
Обновление 10/9/2013: ознакомьтесь с этой интерактивной визуализацией цикла выполнения: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html
Обновление от 09.05.2013: все основные концепции, приведенные ниже, все еще актуальны, но на момент этого коммита реализация Ember Run Loop была выделена в отдельную библиотеку backburner.js с некоторыми очень незначительными различиями в API.
Прежде всего прочтите это:
http://blog.sproutcore.com/the-run-loop-part-1/
http://blog.sproutcore.com/the-run-loop-part-2/
Они не на 100% точны для Ember, но основные концепции и мотивация RunLoop в целом применимы к Ember; отличаются лишь некоторые детали реализации. Но перейдем к вашим вопросам:
Когда запускается Ember RunLoop. Это зависит от маршрутизатора, представлений, контроллеров или чего-то еще?
Все основные пользовательские события (например, события клавиатуры, события мыши и т.д.) запускают цикл выполнения. Это гарантирует, что любые изменения, внесенные в связанные свойства с помощью захваченного события (мышь / клавиатура / таймер и т. Д.), Будут полностью распространены по всей системе привязки данных Ember перед возвратом управления обратно в систему. Итак, перемещение мыши, нажатие клавиши, нажатие кнопки и т. Д. Запускает цикл выполнения.
сколько времени это примерно займет (я знаю, что это довольно глупо спрашивать и зависит от многих вещей, но я ищу общую идею, или, может быть, есть минимальное или максимальное время, которое может занять цикл выполнения)
RunLoop никогда не будет отслеживать, сколько времени требуется для распространения всех изменений в системе, а затем останавливать RunLoop после достижения максимального времени; скорее, RunLoop всегда будет работать до завершения и не остановится до тех пор, пока не будут вызваны все таймеры с истекшим сроком действия, и, возможно, их привязки не будут распространены, и так далее. Очевидно, что чем больше изменений необходимо распространить из одного события, тем больше времени потребуется для завершения RunLoop. Вот (довольно несправедливый) пример того, как RunLoop может увязнуть с распространением изменений по сравнению с другой структурой (Backbone), у которой нет цикла выполнения: http://jsfiddle.net/jashkenas/CGSd5/. Мораль истории: RunLoop действительно быстр для большинства вещей, которые вы когда-либо хотели делать в Ember, и именно в этом заключается большая часть силы Ember, но если вы обнаружите, что хотите анимировать 30 кругов с помощью Javascript со скоростью 60 кадров в секунду, Возможно, это лучший способ сделать это, чем полагаться на RunLoop от Ember.
Выполняется ли RunLoop постоянно или просто указывает период времени от начала до конца выполнения и может не работать в течение некоторого времени.
Он не выполняется все время - он должен в какой-то момент вернуть управление системе, иначе ваше приложение зависнет - это отличается, скажем, от цикла выполнения на сервере, который имеет
while(true)
и продолжается до бесконечности. сервер получает сигнал на выключение ... Ember RunLoop не имеет такого,while(true)
но запускается только в ответ на события пользователя / таймера.Если представление создается из одного цикла RunLoop, гарантировано ли, что все его содержимое попадет в DOM к моменту завершения цикла?
Посмотрим, сможем ли мы это выяснить. Одно из больших изменений от SC к Ember RunLoop заключается в том, что вместо циклического перехода между
invokeOnce
иinvokeLast
(что вы видите на диаграмме в первой ссылке о RL SproutCore) Ember предоставляет вам список «очередей», которые в В ходе цикла выполнения вы можете запланировать действия (функции, которые будут вызываться во время цикла выполнения), указав, к какой очереди принадлежит действие (пример из источника:)Ember.run.scheduleOnce('render', bindView, 'rerender');
.Если вы посмотрите на
run_loop.js
в исходном коде, вы видитеEmber.run.queues = ['sync', 'actions', 'destroy', 'timers'];
, но если вы откроете отладчик JavaScript в браузере в приложении Ember и оценитьEmber.run.queues
, вы получите более полный список очередей:["sync", "actions", "render", "afterRender", "destroy", "timers"]
. Ember сохраняет свою кодовую базу довольно модульной, и они позволяют вашему коду, а также своему собственному коду в отдельной части библиотеки, вставлять больше очередей. В этом случае библиотека Ember Views вставляетrender
иafterRender
ставит в очередь, особенно послеactions
очереди. Я пойму, почему это может быть через секунду. Во-первых, алгоритм RunLoop:Алгоритм RunLoop практически такой же, как описано в статьях цикла выполнения SC выше:
.begin()
и.end()
только в Ember вы захотите вместо этого запустить свой код внутриEmber.run
, который будет внутренне вызыватьbegin
иend
для вас. (Только внутренний код цикла запуска в базе кода Ember все еще используетbegin
иend
, таким образом , вы должны просто придерживатьсяEmber.run
)end()
вызова RunLoop включается, чтобы распространить каждое изменение, сделанное фрагментом кода, переданнымEmber.run
функции. Это включает распространение значений связанных свойств, отображение изменений представления в DOM и т. Д. Порядок, в котором выполняются эти действия (привязка, рендеринг элементов DOM и т. Д.), ОпределяетсяEmber.run.queues
массивом, описанным выше:sync
. Он выполнит все действия, которые были запланированыsync
кодом в очередиEmber.run
. Эти действия могут сами по себе планировать выполнение дополнительных действий во время того же цикла выполнения, и цикл выполнения должен обеспечивать выполнение всех действий до тех пор, пока не будут сброшены все очереди. Это делается так: в конце каждой очереди RunLoop просматривает все ранее очищенные очереди и проверяет, были ли запланированы какие-либо новые действия. Если это так, он должен начать с начала самой ранней очереди с невыполненными запланированными действиями и очистить очередь, продолжая отслеживать свои шаги и при необходимости начинать заново, пока все очереди не будут полностью пусты.В этом суть алгоритма. Вот как связанные данные распространяются через приложение. Вы можете ожидать, что после завершения RunLoop все связанные данные будут полностью распространены. Итак, что насчет элементов DOM?
Здесь важен порядок очередей, в том числе добавленных библиотекой Ember Views. Обратите внимание на это
render
иafterRender
придите послеsync
, иaction
.sync
Очередь содержит все действия для распространения связанных данных. (action
после этого редко используется в источнике Ember). Основываясь на приведенном выше алгоритме, гарантируется, что к тому времени, когда RunLoop попадет вrender
очередь, все привязки данных завершат синхронизацию. Это сделано намеренно: вы бы не хотели выполнять дорогостоящую задачу по отрисовке элементов DOM раньше.синхронизация привязок данных, так как это, вероятно, потребует повторного рендеринга элементов DOM с обновленными данными - очевидно, очень неэффективный и подверженный ошибкам способ очистки всех очередей RunLoop. Таким образом, Ember продуманно выполняет всю возможную работу по связыванию данных перед рендерингом элементов DOM вrender
очереди.Итак, наконец, чтобы ответить на ваш вопрос, да, вы можете ожидать, что все необходимые отрисовки DOM будут выполнены к моменту
Ember.run
завершения. Вот jsFiddle для демонстрации: http://jsfiddle.net/machty/6p6XJ/328/Что еще нужно знать о RunLoop
Наблюдатели против привязок
Важно отметить, что наблюдатели и привязки, обладая аналогичной функциональностью реагирования на изменения в «наблюдаемом» свойстве, ведут себя совершенно по-разному в контексте RunLoop. Распространение
sync
привязки , как мы видели, ставится в очередь для выполнения в RunLoop. Наблюдатели, с другой стороны, запускаются немедленно, когда наблюдаемое свойство изменяется, без необходимости предварительного включения в очередь RunLoop. Если Observer и привязка «наблюдают» за одним и тем же свойством, наблюдатель всегда будет вызываться в 100% случаев до обновления привязки.scheduleOnce
иEmber.run.once
Одно из значительных повышений эффективности в автоматически обновляемых шаблонах Ember основано на том факте, что благодаря RunLoop несколько идентичных действий RunLoop могут быть объединены («устранены», если хотите) в одно действие. Если вы посмотрите на
run_loop.js
внутреннее устройство, вы увидите функции, которые способствуют такому поведению, - это связанные функцииscheduleOnce
иEm.run.once
. Разница между ними не так важна, как знание того, что они существуют, и то, как они могут отбрасывать повторяющиеся действия в очереди, чтобы предотвратить много раздутых и расточительных вычислений во время цикла выполнения.А как насчет таймеров?
Несмотря на то, что «таймеры» являются одной из очередей по умолчанию, перечисленных выше, Ember ссылается только на очередь в своих тестовых примерах RunLoop. Судя по некоторым описаниям из приведенных выше статей о таймерах, которые срабатывают в последнюю очередь, похоже, что такая очередь использовалась бы во времена SproutCore. В Ember
timers
очередь не используется. Вместо этого RunLoop может быть запущен внутренним управляемымsetTimeout
событием (см.invokeLaterTimers
Функцию), которое достаточно интеллектуально, чтобы перебрать все существующие таймеры, запустить все те, у которых истек срок действия, определить самый ранний будущий таймер и установить внутреннийsetTimeout
только для этого события, которое снова запустит RunLoop при срабатывании. Этот подход более эффективен, чем при каждом вызове таймера setTimeout и пробуждении, поскольку в этом случае необходимо выполнить только один вызов setTimeout, а RunLoop достаточно умен, чтобы запускать все разные таймеры, которые могут срабатывать одновременно. время.Дальнейшая дребезг с
sync
очередьюВот фрагмент из цикла выполнения в середине цикла по всем очередям в цикле выполнения. Обратите внимание на особый случай для
sync
очереди: потому чтоsync
это особенно изменчивая очередь, в которой данные распространяются во всех направлениях,Ember.beginPropertyChanges()
вызывается для предотвращения увольнения каких-либо наблюдателей, за которым следует вызовEmber.endPropertyChanges
. Это мудро: если в процессе очисткиsync
очереди вполне возможно, что свойство объекта изменится несколько раз, прежде чем будет опираться на свое окончательное значение, и вы не захотите тратить ресурсы, немедленно увольняя наблюдателей при каждом отдельном изменении. .Надеюсь это поможет. Мне определенно пришлось немного научиться, чтобы написать эту вещь, что было своего рода сутью.
источник