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
). Это единственный процесс, в котором будет выполняться ваше приложение.
Когда этот процесс создан, Дарт автоматически выполняет следующие действия:
- инициализирует две очереди (
Queues
) с именамиMicroTask
(микрозадания)
иEvent
(событие), тип очередейFIFO
(прим.: first in first out, т.е. сообщение,
пришедшие раньше, будут раньше обработаны), - исполняет метод
main()
и, по завершении этого метода - запускает
Event Loop
(цикл событий)
Event Loop представляет собой бесконечный цикл, и если не исполняется никакой другой dart код, то на каждой итерации идет в 1ую очередь запрос исполнения микротаска из очереди микротаска, а потом, во 2ую очередь – запрос исполнения события Event из очереди событий.
Очередь MicroTast
В эту очередь помещаются такие действия, которые должны выполняться асинхронно, сразу после завершения какой либо инструкции, перед тем, как передать управление обратно в Event Loop.
Очередь Event
Фактически, каждый раз при срабатывании внешнего события (таймер, операции ввода/выводы, жесты, рисование), соответствующий код ставится в очередь Event
.
Как только оказывается, что очередь MicroTask
пуста, Event Loop
берёт первую задачу из очереди Event
и исполняет её.
Futures
также обрабатываются с помощью очереди 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
Источники, использованные при написании:
- статья на medium, общая (редирект на нее с сайта dart.dev)
- устаревшая статья на веб архиве с web.archive.org
- https://dart.dev/articles/libraries/dart-io
- https://api.dart.dev/be/169657/dart-async/scheduleMicrotask.html
- оригинал статьи Didier и её перевод на Хабре
- еще нашел вот такую статью – Введение в Dart VM. Сложно и непонятно: https://mrale.ph/dartvm/
Блин, спасибо за статью! Пытался найти презенташку Яндекса, а нигде нет.
Рад, что было полезно!
Вот презентация
https://skondratev.com/files/Dart%20Event%20Loop%20&%20Platform%20Channels.pdf
А slideshare блочит если, заходить с русского ip.