[Solved] How do async/await/then really work in Dart?

Question

Asked by moazelshebly on December 16, 2021 (source).

This is maybe a recurring question but I found conflicting answers and I'm now confused as to which of them is the correct one. I thought I understood the concept then I started reading all of those answers and got totally confused so I'm looking for a definite and simple answer to my question that I could easily comprehend.

As per this answer and this article, await is supposed to interrupt code execution and actually wait for the future to complete and then continue executing the rest of the code sequentially. It also suggests that this might block the main thread, which is only logical in that case.

On the other hand, this, this and this video from the flutter team suggest that await is not going to block the rest of code execution and that it's just syntactical sugar to register callbacks to be executed when the future finishes, which is the same thing that then does.

Now, I tried to write a small program to understand which of them is correct and it seems that the first approach is the way to go:

import 'dart:async';

// prints: 
// 1000+
// 1000+
void main() async {
  Stopwatch watch = Stopwatch();
  
  watch.start();
  
  await Future.delayed(Duration(seconds:1)).then((_){print(watch.elapsedMilliseconds);});
  
  print(watch.elapsedMilliseconds); 
  
}

In opposition to:

import 'dart:async';

// prints:
// 0
// 1000+
void main() async {
  Stopwatch watch = Stopwatch();
  
  watch.start();
  
  Future.delayed(Duration(seconds:1)).then((_){print(watch.elapsedMilliseconds);});
  
  print(watch.elapsedMilliseconds);
  
}

So I just want to know why the flutter team and some people are suggesting that await does not block the code execution and how this concept really works.

Answer

Question answered by moazelshebly (source).

The answer from Andrija is technically correct. However, I still had to think about it a lot until I was able to understand how it really works and this is why I will try to simplify things for anyone who might have the same question.

Suppose you have a dart program; obviously with a main(). Our program calls two functions; foo() and bar().

The function foo() does some asynchronous work, e.g. a network call:

Future<void> foo() async{
  Stopwatch watch = Stopwatch();
  
  watch.start();
  
  await Future.delayed(Duration(seconds:1));
  
  print(watch.elapsedMilliseconds);
}

And the function bar() is a normal function that executes some synchronous code:

void bar() {
  print("Some synchronous code");
}

Now, suppose your main() looks like this:

void main() {
  foo();
  bar();
}

The main program starts and foo() is called -without an await in main()-, we hit the await in foo() and the program goes: "Oh! I'm not supposed to delay the rest of the execution in main(). I gotta register a callback to be executed when the async work is done and go continue the execution of main()". foo() is popped off of the call stack and bar() is then called and prints "Some synchronous work" and is also popped off of the call stack. In the meantime, the async work in foo() finishes and signals completion. This gets picked up by the event loop which goes back to executing the rest of the code in foo() (or the code in the callback if we use .then(); of course if the main thread is not busy.

And this is in simple words what happens. As Andrija suggested, the await blocks the rest of code execution in the same function; the rest of your program will run just fine. If we'd used an await in main() to a-wait for foo(), the execution in main() would've also been blocked until the async work in foo() is done which is not what I had initially thought.

My thinking was that the code in main() will also be delayed based on the await in foo(), which is not the case as we've seen.

ASYNC-AWAIT DART FLUTTER MULTITHREADING
SHARE: