Search code examples
dartasync-awaitfutureevent-loop

Dart: Can't understand how async/awaut, Future and Event loop work together


I'm new to Dart. As i understood from some articles and docs (for example this article):

  1. async/await uses the same mechanism as Future, and that is FIFO Event Loop.
  2. Event Loop launches after the execution of main().
  3. async functions runs synchronously up to the first await keyword. Then they pause the execution, and execution returns to the previous function in call stack.
  4. the remaining code is wrapped to the Future and queued to the Event Loop (most unsure point for me).

Based on these points i expect, that the following code:

main() {
  Future(() => print("1"));
  Future(() {
    print("2");
    asyncFunc("2");
  });
  Future(() => print("3"));

  print("main");
  asyncFunc("main");
  print("\nEvent loop starts?\n");
}

asyncFunc(String from) async {
  print("start  [from $from]");
  await (() async => print("middle [from $from]"))();
  print("end    [from $from]");
}

Will create something similar to this event queue after main() execution:
future1 -> future2 -> future3 -> end_from_main
And after execution of future2, event end_from_future2 will be queued to the end:
future1 -> future2 -> future3 -> end_from_main -> end_from_future2

So result output i expect should be:

main
start  [from main]
middle [from main]

Event loop starts?

1
2
start  [from 2]
middle [from 2]
3
end    [from main]
end    [from 2]

But in fact it returns:

main
start  [from main]
middle [from main]

Event loop starts?

end    [from main]
1
2
start  [from 2]
middle [from 2]
end    [from 2]
3

So the conclusion i made: Either async/await events has priority over Fututre. Either they use diffrent mechanism, unrelated to EventLoop. Or maybe i misunderstand something hardly..


Solution

  • What i think i understood:

    • Future can represent either events in Event queue, either tasks in Microtask queue.
      (for example: default Future() constructor puts event to Event queue, Future.microtask() puts task to Microtask queue)
    • await creates Future.than callback to recived Future.

    My topic example explanation:

    • First three Futrues put events in Event queue.
    • Future<void> created implicitly in await (() async => print("asyncFunc middle"))() creates "empty" task in Microtask queue (i assume that all implicit Futures are scheduled to Microtask queue). Than await puts remaining instructions (print("end [from main]")) as Future.than callback to this "empty" task.
    • When it comes to Event Loop it first executes "empty" task from Microtask queue. Which ended with callback: print("end [from main]").
    • And than Event loop executes Event queue, where second event spawns similar "empty" task in Microtask queue. And that's why end [from 2] happens before third Future