Futures, Isolates, Event loop в Dart/Flutter

Если у вас есть пожелания или дополнения к этой статье: напишите мне в Telegram. Если будет полезно, дополню с указанием ссылкой на вас.
Особенно интересно наглядная анимация тиков в EventLoop, которая будет содержать: синхронные методы, асинхронные, Future и Микротаски.

Посмотрев это видео Илья Вирника, я понял, что мне надо улучшить знания по Event Loop. Взял у Ильи анимацию (а также ссылка на нее на slideshare)

Официальная документация по dart и по flutter по поводу Event Loop довольно разбросана.

Жаль, что я не нашел наглядной анимации выполнения Event loop, такой как в js.

Терминология

Dart – однопоточный язык ( Single Thread), как, например, javascript.

Синхронные операции – “обычные” операции, которые блокируют другие операции до своего выполнения.
Асинхронные операции – операции, которые позволяют другим операциям выполняться, пока они (другие операции) не закончатся.

Синхронные функции – содержат в себе исключительно синхронные операции. Если мы рассматриваем синхронную функцию (или метод класса), то она будет исполняться прямо сейчас до тех пор, пока не завершится.
Асинхронные функции – функции, содержащие в себе хотя бы одну асинхронную операцию.

Асинхронные операции

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

Некоторые распространенные асинхронные операции:

  • получение данных по сети;
  • запись в базу данных;
  • чтение/запись данных в файл.

Такие асинхронные операции обычно представляют результат в виде Future или Stream.

future (экземпляр класса Future) – представляет собой результат асинхронной операции, и может иметь 2 состояния (незавершенное и завершенное):

  • незавершенное (uncompleted);
    • Как только вызывается асинхронная функция (как только компилятор обработал эту функцию), она возвращает незавершенное будущее (uncompleted future). И эта uncompleted future – становится в очередь и мы ждем, пока асинхронная в ней операция выполнится. (В какой именно момент она выполняется – об этом будет написано в статье дальше). Сейчас главное понять, что future попала в очередь и “когда-то там” выполнится.
    • И выполнится она может успешно или с ошибкой (throw an error)
  • завершенное (completed)
    • Успешно завершенное
      • future возвращает тип Future<T> и возвращает значение <T>
      • future возвращает тип Future<void>, то есть ничего не возвращает. Однако, она (как и любая функция) может что-либо вычислять или обрабатывать.
    • Завершенное с ошибкой. Если в процессе выполнения асинхронной операции произошла ошибка, future завершается с ошибкой.

Работа с future c помощью async и await

  • Чтобы определить асинхронную функцию, добавьте async перед {телом} функции;
  • Ключевое слово await работает только в асинхронных функциях. Тем самым мы ожидаем выполнения этой асинхронной функции. И пока не выполнится эта функция, следующая не будет обработана.

Другими словами использование async await заставляет компилятор выполнять асинхронный код последовательно, “типо как будто это синхронный код”.

Пример 1:


Future takePizza() async {
  print(2);
  var order = await preparePizza();
  print(4);
  return 'Ваш заказ: $order';
}

Future preparePizza() {
  print(3);
  return Future.delayed(
    const Duration(seconds: 2),
    () => 'Пицца приготовилась. Она готовилась 2 секунды',
  );
}

Future main() async {
  print(1);
  print(await takePizza());
  print(5);
}

//выведет:
//1
//2
//3
//4
//Ваш заказ: Пицца приготовилась. Она готовилась 2 секунды
//5
//Exited

Как видно из примера мы последовательно ждем когда выполнится наш заказ (приготовится пицца) и только потом переходим к выполнению следующей функции (или строчки кода). Да, и программа в этот момент ожидания “зависает”, т.к. мы указали await.

Пример 2.



void main(List arguments) {
  print(1);
  Future(() {
    print('future by main');
  });
  myFunc();
  print(2);
}

myFunc() {
  print('sinc 1 by myFunc');
  Future(() {
    print('future by myFunc');
  });
  print('sinc 2 by myFunc');
}

//OUTPUT
// 1
// sinc 1 by myFunc
// sinc 2 by myFunc
// 2
// future by main
// future by myFunc

Пример 3.


void main(List arguments) async {
  print(1);
  await Future(() {
    print('future by main');
  });
  myFunc().then((value) {
    print(value);
  });
  print(2);
}

Future myFunc() {
  print('sinc 1 by myFunc');
  Future(() {
    print('future 1 by myFunc');
  });
  print('sinc 2 by myFunc');
  return Future(() => 'future 2 by myFunc');
}

//OUTPUT
// 1
// future by main
// sinc 1 by myFunc
// sinc 2 by myFunc
// 2
// future 1 by myFunc
// future 2 by myFunc

Модель исполнения в Dart

Компонент Dart, который управляет очерёдностью исполнения инструкций, называется Event Loop.

Когда вы запускаете Flutter-приложение (и любое Dart-приложение), создается и запускается новый процесс — Thread, в терминах Дарта — Изолят (Isolate). Это единственный процесс, в котором будет выполняться ваше приложение.

Когда этот процесс создан, Дарт автоматически выполняет следующие действия:

  1. инициализирует две очереди (Queues) с именами MicroTask (микрозадания)
    и Event (событие), тип очередей FIFO (прим.: first in first out, т.е. сообщение,
    пришедшие раньше, будут раньше обработаны),
  2. исполняет метод main() и, по завершении этого метода
  3. запускает Event Loop (цикл событий)

Event Loop представляет собой бесконечный цикл, и если не исполняется никакой другой dart код, то на каждой итерации идет в 1ую очередь запрос исполнения микротаска из очереди микротаска, а потом, во 2ую очередь – запрос исполнения события Event из очереди событий.

Очередь MicroTast

В эту очередь помещаются такие действия, которые должны выполняться асинхронно, сразу после завершения какой либо инструкции, перед тем, как передать управление обратно в Event Loop.

Очередь Event

Фактически, каждый раз при срабатывании внешнего события (таймер, операции ввода/выводы, жесты, рисование), соответствующий код ставится в очередь Event.

Как только оказывается, что очередь MicroTask пуста, Event Loop берёт первую задачу из очереди Event и исполняет её.

Futures также обрабатываются с помощью очереди Event.

цикл событий (Event Loop) обрабатывает одно событие за раз
Event Loop обрабатывая событие, сдвигает очередь (Event queque), переходя к следующему событию
Важно понимать, что существуют 2 очереди: Microtask и Event. Обе очереди выполняются в порядке FIFO. У очереди Microtask приоритет выполнения выше, чем у Event.

Futures

Future представляет собой задачу, которая выполняется асинхронно и завершается (успешно или с ошибкой) когда-то в будущем.
Что происходит, когда вы создаёте экземпляр Future:

  • экземпляр создаётся и хранится во внутреннем массиве, управляемом Dart
  • код, который должен быть исполнен данным экземпляром Future, добавляется
    напрямую в очередь Event
  • возвращается экземпляр Future со статусом не завершено (incomplete) ?

Код функции, помеченной async будет выполняться синхронно до тех пор, пока не встретится ключевое слово await.

Пример кода:


void main() {
  print('Before the Future');
  Future(() {
    print('Running the Future');
  }).then((_) {
    print('Future is complete');
  });
  print('After the Future');
}
// отобразится:
// Before the Future
// After the Future
// Running the Future
// Future is complete

Код Future НЕ выполняется параллельно, он выполняется в последовательности, определяемой Event Loop

Примеры кода поломать голову и проверить свои знания

Пример 1

То напишите, что выведет код 1 и код 2. И сравните ваши результаты и выполнение кода – именно эти примеры у Илья Вирника в презентации.

Возможно вас успокоит, что в сам flutter всего лишь 7 раз его использует микротаски.

Пример 2

Тут вызываются асинхронные методы без async. Понять последовательность выполнения команд в этом случае очень сложно. Вывод один – не усложнять настолько свой код, чтобы его выполнение было невозможно предугадать

https://dartpad.dev/?id=09406a6959252d07bbe51f404fcecf1f&null_safety=true

https://dartpad.dev/?id=e9f6b1b9ca6a02f32fd182a1b0a86dbf

Еще один пример – вызываем async без await

Источники, использованные при написании:

Рубрика: dart

2 ответа к «Futures, Isolates, Event loop в Dart/Flutter»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *