There was a time when I made a code with this structure while making a code.
void main() async {
print('one');
first();
print('five');
}
void first() async {
print('two');
await second();
print('four');
}
Future<void> second() async {
print('three');
}
Above code's result was
one two three five four
And I was very confused by this result. I thought the await syntax would wait for the second function and output "four" right away.
However, "five" was printed at fourth and "four" was printed at the right after.
Is there anyone who can tell me why "five" is printed at fourth sequence?
Edit 1.
My initial question was ambiguous, so I add a new explanation.
When I executed below code, the output sequence is
one two three four five.
void main() async {
print('one');
first();
print('five');
}
void first() async {
print('two');
second();
print('four');
}
Future<void> second() async {
print('three');
}
Yeah that output is so reasonable for me, because print('two')
, await second()
print('four')
is in same function's - first()
- scope.
But, in the case of await second()
, why print('fout')
is not executed right after await second()
??
Why await
statement break the function scope??
This is my real curiosity...
CodePudding user response:
I think where you get confused is that when we call async
functions, we run them synchronously until first await
:
Note: Although an async function might perform time-consuming operations, it doesn’t wait for those operations. Instead, the async function executes only until it encounters its first await expression (details). Then it returns a Future object, resuming execution only after the await expression completes.
https://dart.dev/guides/language/language-tour#handling-futures
If we go thought the first posted example, it is executed in the following order:
- Print "one".
- Calls
first()
and continues the execution inside here. - Print "two".
- Calls
second()
and continues the execution inside here. Theawait
statement is first coming into play when we return fromsecond()
which will give us aFuture<void>
toawait
on. - Print "three".
second()
returns aFuture<void>
which is generated automatically from the fact that the method is markedasync
. In this example, theFuture
are already completed with a value but the laterawait
are still going to move further executing ofsecond()
into a new event on an event queue.- We
await
theFuture<void>
fromsecond()
. Since this is the firstawait
infirst()
we will now return aFuture<void>
fromfirst()
. Yes, this happens even if the method is marked to returnvoid
so by usingvoid
you are really just making it "impossible" for the caller toawait
theFuture
. - We are not back at
main()
and continues to run the rest of this method. Which means we print "five". - Since we don't have more code to execute, we check the microtask event queue (and afterwards if nothing is found event-queue) for any events to be executed. These events is added when a e.g.
Future
gets completed with a value which happen immediately whensecond()
was done. - The first (and only) event to execute are one that continues the execution of
first()
from the point ofawait second();
. We now print "four".
CodePudding user response:
When you don't await the Future returned from an async function, then any non-async code within that function will be executed immediately and synchronously.
So, when you remove all awaits, the effect is that you don't have any code executing asynchronously - it's the same result as if none of the functions were marked async
.
The behavior you saw in the first example was because you did not use await
in main
, but you did use await in first
. This changes the order of execution, such that the code after await
in first
will be deferred until after all the synchronously executable code has finished executing.
It might be more clear if, instead of separating these into separate methods, we expand the methods such that all of the code is contained within main
, and also replace the usage of await with .then()
.
For your first example:
void main() async {
print('one'); // Executes immediately
print('two'); // Executes immediately
(() async {
print('three'); // Executes immediately
})().then((_) {
print('four'); // Execution deferred
});
print('five'); // Executes immediately
}
Whereas your second example expands straightforwardly:
void main() async {
print('one');
// Begin first()
print('two');
// Begin second()
print('three');
// End second()
print('four');
// End first()
print('five');
}