Ответ
В Dart макрозадачи (macrotasks) — это задачи, которые планируются в очередь событий (event queue) и выполняются в следующих итерациях Event Loop. Они имеют более низкий приоритет, чем микрозадачи (microtasks).
Типичные примеры макрозадач:
- Обработка внешних событий: Пользовательский ввод (тапы, жесты), таймеры (
Timer.run,Future.delayed), сообщения от операционной системы. - I/O-операции: Завершение сетевого запроса, чтение/запись файла.
- Завершение Future: Коллбэк, переданный в
Future(() => ...)илиthen()(если Future еще не завершен), выполняется как макрозадача.
Порядок выполнения Event Loop:
- Выполняется синхронный код до конца.
- Обрабатывается вся очередь микрозадач (microtask queue) до опустошения.
- Берется и выполняется одна задача из очереди событий (event queue).
- Возврат к шагу 2 (проверка микрозадач).
Пример, демонстрирующий порядок:
void main() {
print('1. Start (синхронный код)');
// Помещается в очередь событий (макрозадача)
Future(() => print('4. Future (macrotask from event queue)'));
// Помещается в очередь микрозадач
scheduleMicrotask(() => print('3. Microtask'));
// Синхронный код продолжает выполняться
print('2. End (синхронный код)');
// Event Loop начнет работу после завершения main()
}
Вывод:
1. Start (синхронный код)
2. End (синхронный код)
3. Microtask
4. Future (macrotask from event queue) Ответ 18+ 🔞
А, ну это же про Event Loop в Dart! Ёпта, вещь, которая сначала кажется простой, а потом такую мозгоправку устраивает, что сам от себя охуеешь. Слушай, представь себе такую картину.
Есть у тебя этот самый Event Loop — типа главный диспетчер, который решает, что и когда выполнять. И у него две основные очереди: одна для микрозадач (microtasks), а другая — для макрозадач (macrotasks). Вот макрозадачи — это как раз те самые, которые попадают в очередь событий (event queue) и выполняются не сразу, а в следующих итерациях цикла. Приоритет у них, скажем так, пониже, чем у микрозадач.
Что обычно туда летит, в эту очередь макрозадач? Да всякая классика, которую ты уже сто раз видел:
- Всякий внешний шум: Пользователь ткнул в экран, таймер сработал (
Timer.run,Future.delayed), система прислала какую-то фигню. - Ввод-вывод: Сетевой запрос приполз, файл прочитался.
- Завершение Future: Коллбэк от
Future(() => ...)или отthen(), если Future ещё не был готов, — тоже макрозадача.
А теперь, блядь, самое важное — порядок работы этого цикла. Запоминай, а то потом будешь сидеть и думать, почему у тебя всё выполняется не в том порядке, в каком ты ожидал.
- Сначала он выгребает весь синхронный код до самого конца. Пока не упрётся.
- Потом он лезет в очередь микрозадач и выпиливает всё до дна, пока она не опустеет. Это железно.
- Только после этого он берёт одну-единственную задачу из очереди событий (то есть макрозадачу) и выполняет её.
- А потом — о, хитрая жопа! — он снова возвращается к шагу 2, проверяет микрозадачи, и если они там накопились, опять их все вычищает. И так по кругу.
Вот тебе наглядный пример, чтобы в голове всё встало на свои места:
void main() {
print('1. Start (синхронный код)');
// Эта штука полетит в очередь событий (макрозадача)
Future(() => print('4. Future (macrotask from event queue)'));
// А эта — прямиком в очередь микрозадач
scheduleMicrotask(() => print('3. Microtask'));
// Синхронный код продолжает работать
print('2. End (синхронный код)');
// И только когда main() полностью отработает, Event Loop начнёт свою движуху.
}
И что же мы увидим на выходе? Вот это:
1. Start (синхронный код)
2. End (синхронный код)
3. Microtask
4. Future (macrotask from event queue)
Вот и вся магия. Сначала весь синхронный код, потом ВСЕ микрозадачи, и только потом по одной начинают вытаскиваться макрозадачи. Запомни эту последовательность, и половина твоих будущих "а оно нихуя не работает как я задумал" отпадёт сама собой.