краткий перевод: Understanding Zones and Change Detection in Ionic 2 & Angular 2

Зоны и change detection наиболее важная часть Angular 2. Вообщем то мне казалось что я немного понимаю как работает change detection, но в действительно я так и не разобрался с тем как там все устроено. Я решил провести небольшое расследование что бы понять как это работает, и это статья моя попытка суммировать то что я открыл для себя прочитав много статей написаных людьми более опытными чем я.

Это статья содержит простое введение в зоны и описание того как они важны для change detection в Angular 2 и Ionic 2.

Что такое Change Detection?

Pascal Precht описывает задачу change detection как:

Базовая задача change detection это взять внутреннее состояние программы и отобразить его в пользовательском интерфейсе.

Другое имя для change detection “dirty checking”(грязная проверка ). В сравнение с Angular 1, change detection в Angular 2 выглядит как магия. В общем, что то изменятся и Angular 2 каким то образом узнает это и обновляет соотвествующую вьюху. Конечно же, это не магия, существует вполне логическое объяснение которое мы собирается рассказать в этой статье.
Angular 2 запускает внутри себя специальную зону называемую NgZone. Запуск внутри зоны позволяет определять запуск асинхронных задач – то есть позволяет изменять внутреннее состояние приложения, и соотвествующие вьюхи – запуск и завершение. Так как причины изменений во вьюхах и есть эти асинхронные задачи, то определяя когда они выполняются Angular 2 знает что вьюха возможно нуждается в обновление.

Что такое Зоны(Zones)?

Как я заметил выше Angular 2 запускает специальную зону NgZone. Это специальная зона расширяет базовую функциональность зон для помощи в change detection. Но что такое зоны ?

Зоны это не уникальная концепция для Angular 2. Зоны могут быть добавлены в любое Javascript приложение с подключенным библиотекой Zone.js, а проект описывается как:

…контекст исполнения внутри которого исполняются асинхронные задачи. Вы можете думать об этом как о хранилище локальных потоков для JavaScript VMs.

Я думаю это хорошая аналогия, но тут было высказаны несколько концепций понимание которых зависят от вашего уровня знаний в Javascript и программирования в целом. Поэтому позвольте объяснить о чем идет речь.

Во первых, начнем с простого. Асинхронные задачи это задачи выполняющиеся вне основного программного потока – то есть программа будет выполняться без ожидания завершения асинхронной задачи. Когда асинхронная задача завершится приложение сможет обработать завершение например запустить что типа callback или promise. Все асинхронные задачи в Javascript добавляются в очереди событий, и задачи в очереди событий выполняются циклом обработки событий в свое время (то есть когда стек пустой, цикл обработки событий поставит задачу в стек):

event-loop

Картинка выше визуализирует это. Нормальные (синхронные) функции вызываются программой через добавление в “stack”, который выполняет их сверху вниз. Любые асинхронные функции вызываются добавлением в “event queue”. Как только стек пустой, задачи в очереди могут начать исполнение.
JavaScript Virtual Machine (VM), или движок JavaScript, это программа которая исполняет код JavaScript. В разные браузеры встроенные разные движки которые интерпретируют и исполняют код JavaScript по разному, например движок от Google V8 и движок от Apple Nitro. Для нас это будет одинаково, но разные движки имеют (небольшие) различия в интерпритации синтаксиса и в скорости выполнения.

Локальное хранилище потоков это программная концепция которая подразумевает создание отдельных областей памяти для определенных потоков. Поток это код который запущен независимо от другого кода и выполняющиеся одновременно. Поэтому “поток” это тоже самое что и асинхронная задача в Javascript. Это важно для операционных систем (и других вещей) потому что это позволяет компьютерам быть мультизадачными. Вы можете иметь 5 и более приложений на вашем компьютере, которые запущены разными процессами. Мультипоточность позволяет совместное использование вычислительной мощности, переключаясь между различными потоками, создавая иллюзию что компьютер делает несколько задач одновременно. В действительности ваш компьютер делает одну вещь в одно время (если у вас один процессор), но он это делает в очень небольшом отрезке времени.

Запуск разных потоков выполняющих одну и туже логику в одно и то же время создает идеальную среду для конфликтов. Если одному потоку требуется для хранения глобальные переменные а другому в это же время потребовались те же переменные в результате использование переменных будет непредсказуемо. Локальное хранилища потоков позволяют каждому потоку иметь собственное глобальное пространство для хранения переменных независимо от других потоков.

Вы можете думать о зонах как о пути создания независимого пространства одного кода от другого.
Так почему же зоны как независимые контексты выполнения очень важны для change detection?
Важной частью является то что зоны “сохраняются при выполнение асинхронных задач”, поэтому асинхронная задача будет выполнятся в той же зоне где она была создана. Зоны не только позволяют выполнять код в их собственном маленьком мире но так же позволяют вклиниваться в них так что бы определять когда асинхронная задача началась и когда закончилась.

Я думаю статья Pascal Precht Understanding Zones очень хорошо объясняет это, я надеюсь он не против если я позаимствую следующий  пример:

Здесь мы выполняем несколько функций, и у нас есть таймер для определения времени выполнения задачи. Проблема в приведенном коде в том что мы используем setTimeout который создает асинхронную задачу которая будет добавлена в очередь событий event queue. Код не будет ждать 2 секунды что бы завершить выполнение вместо этого будет выполнен bar() а потом baz() а потом будет остановлен таймер – все это до того как будет выполнена задача doSomething. Поэтому невозможно определить точно время выполнения.

Представьте Ionic 2 приложение – если мы создали наше сразу после выполнения кода, как нам узнать что то измениться после выполнения doSomething, если нам не нужно что то вызывать из doSomething для уведомления о том что были изменения.

Давай те взглянем на другой пример из той статьи:

Мы создали новую зону копируя родительскую зону с параметрами “myZoneSpec”. Это myZoneSpec использует beforeTask и afterTask функции, которые могут определить когда асинхронная задача стартует и завершается внутри зоны. Мы можем их использовать для определения времени выполнения кода , включая асинхронную функцию doSomething. Как только мы создали нашу специальную зону, мы запустили программы внутри myZone.run. Это по существу то что делает Angular 2 создавая его NgZone зону и запускает процесс change detection.

Поэтому в любое время когда задача запущена внутри контекста зоны, мы можем определить когда она стартовала а когда она завершится.

Как Change Detection работает?

Сейчас вы имеете представление о том как change detection работает. Angular 2 имеет собственную зону – его собственный контекст выполнения – и он может определять когда любая асинхронная задача выполняется стартует или завершается внутри зоны. Поэтому любая задача которая выполняется внутри Angular 2 запустит триггер наличие изменений. Это важная концепция для понимания, потому что все что запущенно вне зоны Angular 2 не запустит триггер изменений.

Мы собираемся пойти немного глубже в понимание того как это работает.

Когда мы первый раз запускаем наше приложение наш код запуститься. Корневой компонент создаться и запуститься, будут созданы наши компоненты, все конструкторы всех функций выполнять их код и т.д. В конце концов, все уравновесится (стек будет пустой), наше приложение достигнет состояние стабильности.

Приложение в котором нет изменений достаточно скучное приложение, вообще то можно сказать что это не приложение вовсе! И так как только приложение запуститься есть несколько способов определения изменений – все из которых вызываются асинхронными задачами, которыми могут быть:

  • Events например (click)
  • Http Requests например http.post
  • Timers например setTimeout

Событие клика на кнопку может случится в любое время, и это может быть причинной изменений во вьюхе. HTTP запрос может запустить изменения в приложение немедленно или через 10 секунд, и это так же может потребовать изменений во вьюхе.

Давай те взглянем на следующий пример:

В этом примере вы инициировали назначение переменной myTitle значением “Hello”. Поэтому как только наше приложение запуститься, myTitle будет иметь значение “Hello”.

У нас есть таймер, и после 5 секунд приложение изменит значение myTitle на “Goodbye!”. Проблема в следующем: как Angular узнает об изменение? Хорошо, мы уже знаем что он использует зоны для этого, но как?

Как я уже говорил, NgZone это специальный тип зон создаваемый Angular 2. Он копирует его родительскую зону, которая позволяет установить его собственную фунциональность в этой зоне. Это дополнительная функциональность включает добавление события onTurnDone (схожего с afterTask), которое запускается когда зона Angular завершит процессинг текущего хода – ход это то что случается когда цикл событий event loop проталкивает задачу из очереди событий event queue (асинхронную задачу) в стек.
Если мы взглянем на следующий пример кода из Zones в Angular 2:

мы увидем что каждый раз когда “ход” завершается в зоне Angular запускается функция tick. Если существуют асинхронные задачи, то как только стек станет пустым они будут добавлены в стек и выполнены – как только они завершаться Angular будет оповещен о том что задача завершена. Это функция запускает change detection для каждого компонента каждый раз когда завершается “ход”. Каждый компонент в Angular 2 имеет собственный change detection, поэтому не нужно проходит все дерево компонентов при каждом изменение. По сути дела, каждый раз когда асинхронная задача завершена, Angular запускает функцию tick которая проверяет наличие изменений.

Главное помнить для того чтобы вызвать проверку наличие изменений код должен выполняться внутри зоны Angular. Если вы разрабатываете Ionic 2 приложение вы можете использовать NgZone напрямую:

это запустит change detection механизм для проверки наличие изменений.

Так же мы можем запустить некоторый код в случае необходимости вне зоны Angular (для исключения из механизма определения изменений):

 

It's only fair to share...Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedInShare on VK