Что такое Event Loop в Dart?

Ответ

Event Loop — это механизм, который управляет выполнением асинхронного кода в Dart. Он работает в рамках одного изолята (isolate) и обеспечивает неблокирующую обработку операций, таких как ввод-вывод (I/O), таймеры или жесты пользователя.

Основные компоненты и порядок работы:

  1. Синхронный код: Выполняется сразу и до конца.
  2. Очередь микрозадач (Microtask Queue): Обрабатывается перед очередью событий. Сюда попадают задачи из scheduleMicrotask() и завершения Future (через .then() или await).
  3. Очередь событий (Event Queue): Сюда попадают «внешние» события: таймеры (Future.delayed), операции ввода-вывода (ответы от сети, чтение файлов), взаимодействия с UI.

Алгоритм Event Loop:

while (true) {
  // 1. Выполнить все микрозадачи из очереди микрозадач.
  while (microtaskQueue.isNotEmpty) {
    microtaskQueue.removeFirst().call();
  }
  // 2. Если очередь событий не пуста, выполнить одно событие.
  if (eventQueue.isNotEmpty) {
    eventQueue.removeFirst().call();
  }
  // 3. Если обе очереди пусты — ожидать новых событий.
}

Практический пример, демонстрирующий порядок:

void main() {
  print('1. Синхронный код');

  Future(() => print('3. Event Queue (Future)')).then((_) {
    print('4. Microtask из .then() предыдущего Future');
  });

  scheduleMicrotask(() => print('2. Microtask Queue'));

  print('5. Снова синхронный код');
}

Вывод будет:

1. Синхронный код
5. Снова синхронный код
2. Microtask Queue
3. Event Queue (Future)
4. Microtask из .then() предыдущего Future

Зачем это важно в Flutter? Понимание Event Loop помогает писать отзывчивые UI-приложения. Длительные синхронные операции «замораживают» интерфейс, потому что блокируют цикл событий. Поэтому тяжелые вычисления нужно выносить в отдельный изолят или разбивать на асинхронные задачи.

Ответ 18+ 🔞

Давай разберемся, что это за зверь такой — Event Loop в Dart. Представь, что твой изолят — это одинокий офисный клерк, а Event Loop — это его ебаный рабочий день, расписанный по минутам. И этот клерк — трудоголик, он не может делать две вещи сразу, но зато умеет грамотно переключаться между срочным и важным.

Из чего состоит его день:

  1. Синхронный код. Это как его прямые обязанности из должностной инструкции. Пришел на работу — сел и делает, пока не закончит. Никаких «подожди пять минут».
  2. Очередь микрозадач (Microtask Queue). Это его личные, но жутко срочные напоминалки. Типа «сразу после текущего дела позвони мамке» или «перешли срочный файл». Он проверяет и выполняет их моментально, как только заканчивает с текущим пунктом, и делает ВСЕ до одной, прежде чем взяться за что-то другое.
  3. Очередь событий (Event Queue). Это уже входящие задачи из внешнего мира. Письма на почту, напоминание о встрече через час, уведомление, что скачался файл. Он берется за них только тогда, когда разделался со ВСЕМИ микрозадачами, и берет по одной за раз.

Алгоритм его вечного рабочего дня (пока приложение живо) выглядит так:

while (true) {
  // 1. Сначала выгребаем все до единой срочные микрозадачи.
  while (microtaskQueue.isNotEmpty) {
    microtaskQueue.removeFirst().call(); // Выполняем одну
  }
  // 2. Если микрозадач нет, берем одну задачу из основной очереди.
  if (eventQueue.isNotEmpty) {
    eventQueue.removeFirst().call(); // Выполняем одну
  }
  // 3. Если всё чисто — сидим, боремся с выгоранием и ждем новых поручений.
}

Давай на живом примере, а то нихуя не понятно. Смотри, что выведет этот код:

void main() {
  print('1. Синхронный код — делаю сразу и начисто.');

  // Это кинули в общую папку "входящие" (Event Queue)
  Future(() => print('3. Event Queue (Future)')).then((_) {
    // А это — срочная напоминалка, которая создастся ПОСЛЕ выполнения того Future
    print('4. Microtask из .then() предыдущего Future');
  });

  // А это я сам себе оставил срочную записку (Microtask Queue)
  scheduleMicrotask(() => print('2. Microtask Queue'));

  print('5. Снова синхронный код — инструкцию надо выполнять до конца.');
}

И вот как бедолага-клерк это обработает:

1. Синхронный код — делаю сразу и начисто.
5. Снова синхронный код — инструкцию надо выполнять до конца.
(Синхронный код кончился. Так, проверяю срочные записки!)
2. Microtask Queue
(Микрозадач больше нет. Теперь смотрю входящие.)
3. Event Queue (Future)
(Выполнил одну задачу из очереди. Опа, после неё появилась новая микрозадача! Срочную записку создали. Возвращаюсь к пункту 1 алгоритма — выгребаю микрозадачи.)
4. Microtask из .then() предыдущего Future

Нахуя это всё в Flutter? Да затем, что если ты заставишь этого клерка делать какую-то ебанутую синхронную вычислятину на 5 секунд (типа перемножить миллиард матриц), то он на это время полностью заблокируется. Он не будет проверять ни срочные записки, ни входящие. А в мире Flutter входящие — это, прости господи, нажатия кнопок, анимации и обновления UI. Получится, что твой интерфейс просто встанет колом, и пользователь подумает, что приложение накрылось медным тазом. Поэтому всё, что требует времени, надо либо грамотно дробить и отдавать через Future (кидать в очередь), либо, если задача совсем ядрёна вошь, выносить в отдельный изолят — нанимать второго клерка, чтобы основной не ебал себе мозг и мог реагировать на действия пользователя.

Видео-ответы