Що таке Ember RunLoop і як він працює?


96

Я намагаюся зрозуміти, як працює Ember RunLoop і що робить її галочкою. Я переглянув документацію , але все ще маю багато питань щодо неї. Мені цікаво краще зрозуміти, як працює RunLoop, щоб я міг вибрати відповідний метод у своєму просторі імен, коли мені доведеться відкласти виконання деякого коду на більш пізній час.

  • Коли починається Ember RunLoop. Це залежить від маршрутизатора, подання, контролерів чи чогось іншого?
  • скільки часу це приблизно займає (я знаю, що це досить безглуздо запитувати і залежати від багатьох речей, але я шукаю загальну ідею, або, можливо, якщо пробіг може зайняти мінімальний або максимальний час)
  • Чи виконується RunLoop постійно або це просто вказівка ​​періоду часу від початку до кінця виконання та може не працювати протягом деякого часу.
  • Якщо представлення створюється з одного RunLoop, чи гарантується, що весь його вміст перетворить його в DOM до моменту закінчення циклу?

Вибачте мене, якщо це дуже основні питання, я думаю, розуміння цих питань допоможе таким неблагонадійним людям, як я, краще використовувати Ember.


5
Немає чудових документів про цикл запуску. Я спробую на цьому тижні зібрати на ньому коротку гірку.
Luke Melia

2
@LukeMelia це питання все ще відчайдушно потребує вашої уваги, і схоже, що деякі інші люди шукають ту ж інформацію. Було б чудово, якщо у вас є можливість, поділитися своєю думкою про RunLoop.
Арас,

Відповіді:


199

Оновлення 9.10.2013: Ознайомтеся з цією інтерактивною візуалізацією циклу запуску: https://machty.s3.amazonaws.com/ember-run-loop-visual/index.html

Оновлення 9.5.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% точні до Ембер, але основні концепції та мотивація, що стоять за RunLoop, як правило, стосуються Ембер; лише деякі деталі реалізації відрізняються. Але, щодо ваших запитань:

Коли починається 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 вище:

  • Ви запускаєте свій код між RunLoop .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 повинен забезпечити, щоб він виконував кожну дію, доки всі черги не будуть очищені. Це робиться так, що наприкінці кожної черги RunLoop переглядає всі попередньо очищені черги та перевіряє, чи не було заплановано нових дій. Якщо так, то він повинен починатись на початку самої ранньої черги з невиконаними запланованими діями та змивати чергу, продовжуючи відстежувати її кроки та починати спочатку, доки всі черги повністю не порожні.

У цьому суть алгоритму. Ось як пов’язані дані поширюються через додаток. Ви можете очікувати, що як тільки RunLoop запуститься до кінця, всі пов'язані дані будуть повністю розповсюджені. Отже, як щодо елементів DOM?

Тут важливий порядок черг, включаючи ті, що додаються бібліотекою Ember Views. Зауважте, що renderі afterRenderприходьте після sync, і action. syncЧерга містить всі дії для поширення пов'язаних даних. ( actionпісля цього лише рідко використовується в джерелі Ембер). На основі вищезазначеного алгоритму гарантується, що до моменту, коли RunLoop потрапить до renderчерги, усі прив'язки даних завершать синхронізацію. Це дизайн: ви не хочете , щоб виконати завдання дорогу рендеринга DOM елементів , перш ніжсинхронізація прив’язок даних, оскільки для цього, ймовірно, знадобиться повторний рендерінг елементів DOM з оновленими даними - очевидно, дуже неефективний і схильний до помилок спосіб очищення всіх черг RunLoop. Тож Ember розумно перевіряє всю роботу з прив’язки даних, яку може, перед тим, як відображати елементи DOM у renderчерзі.

Отже, нарешті, щоб відповісти на ваше запитання, так, ви можете очікувати, що будь-які необхідні візуалізації DOM відбудуться до моменту Ember.runзакінчення. Ось jsFiddle для демонстрації: http://jsfiddle.net/machty/6p6XJ/328/

Інші речі, які слід знати про RunLoop

Спостерігачі проти прив’язок

Важливо зазначити, що спостерігачі та прив'язки, маючи подібну функціональність реагування на зміни у властивості "спостерігається", поводяться абсолютно по-різному в контексті RunLoop. Як ми вже бачили, розповсюдження прив’язки планується до syncчерги, щоб врешті-решт виконати його RunLoop. З іншого боку, спостерігачі запускають негайно, коли спостерігається властивість змінюється, без необхідності попереднього планування в чергу RunLoop. Якщо спостерігач і прив'язка "спостерігають" за одним і тим же властивістю, спостерігач завжди буде викликаний на 100% раніше, ніж прив'язка буде оновлена.

scheduleOnce і Ember.run.once

Одне з найбільших підвищення ефективності в шаблонах автоматичного оновлення Ember базується на тому, що завдяки RunLoop кілька однакових дій RunLoop можна об'єднати ("зняти", якщо хочете) в одну дію. Якщо ви подивіться в run_loop.jsнутрощі, ви побачите , що функції , які полегшують таку поведінку є відповідними функціями scheduleOnceі Em.run.once. Різниця між ними не настільки важлива, як те, що вони знають, і те, як вони можуть відкидати дублікати дій у черзі, щоб запобігти великій кількості роздутих, марнотратних розрахунків під час циклу запуску.

А як щодо таймерів?

Незважаючи на те, що "таймери" є однією з перелічених вище черг за замовчуванням, Ember робить посилання на чергу лише у своїх тестових випадках RunLoop. Здається, така черга використовувалася б у дні SproutCore на основі деяких описів із вищезазначених статей про те, що таймери були останніми, що спрацьовували. В Ембер timersчерга не використовується. Натомість RunLoop може бути запущений внутрішньо керованою setTimeoutподією (див. invokeLaterTimersФункцію), яка є досить розумною, щоб прокрутити всі існуючі таймери, запустити всі, що закінчились, визначити найраніший майбутній таймер та встановити внутрішнійsetTimeoutлише для цієї події, яка знову запустить RunLoop, коли вона спрацює. Цей підхід є більш ефективним, ніж кожен виклик таймера setTimeout і пробудження, оскільки в цьому випадку потрібно зробити лише один виклик setTimeout, а RunLoop досить розумний, щоб запускати всі різні таймери, які можуть одночасно спрацьовувати час.

Подальше зняття з syncчерги

Ось фрагмент циклу циклу, посередині циклу через усі черги циклу циклу. Зверніть увагу на особливий випадок syncчерги: оскільки syncце особливо нестабільна черга, в якій дані поширюються в будь-якому напрямку, Ember.beginPropertyChanges()викликається, щоб запобігти звільненню будь-яких спостерігачів, за яким слід виклик Ember.endPropertyChanges. Це розумно: якщо під час очищення syncчерги цілком можливо, що властивість об’єкта зміниться кілька разів, перш ніж зупинитися на його кінцевій вартості, і ви не хотіли б витрачати ресурси, негайно звільняючи спостерігачів за кожну окрему зміну .

if (queueName === 'sync') 
{
    log = Ember.LOG_BINDINGS;

    if (log) 
    {
        Ember.Logger.log('Begin: Flush Sync Queue');
    }

    Ember.beginPropertyChanges();
    Ember.tryFinally(tryable, Ember.endPropertyChanges);

    if (log) 
    { 
        Ember.Logger.log('End: Flush Sync Queue'); 
    }
} 
else 
{
   forEach.call(queue, iter);
}

Сподіваюся, це допомагає. Мені, безумовно, довелося навчитися зовсім небагато, щоб просто написати цю річ, що було якоюсь суттю.


3
Чудова запис! Я чую чутки про те, що "спостерігачі негайно стріляють" в якийсь момент можуть змінитися, щоб зробити їх затримкою, як прив'язку.
Джо Лісс

@JoLiss так, я відчуваю, що я чув про це кілька місяців ... не впевнений, що / коли це
вдасться

1
Брендан Бриггс виступив із чудовою презентацією про Run Loop на зустрічі в Нью-Йорку в січні 2014 року. Відео тут: youtube.com/watch?v=iCZUKFNXA0k
Luke Melia

1
Ця відповідь була найкращим ресурсом, який я знайшов про Ember Run Loop, дуже гарна робота! Нещодавно я опублікував обширний підручник про Run Loop, заснований на вашій роботі, який, сподіваюся, описує ще більше деталей цього механізму. Доступно тут на.netguru.co/ember-ebook-form
Kuba Niechciał
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.