What's difference between Future.value
vs Future.microtask
Case1:
Future.microtask(() => 1).then(print);
Future.microtask(() => Future(() => 2)).then(print);
Future.value(3).then(print);
Future.value(Future(() => 4)).then(print);
Output for this is:
1
3
4
2
Case2: And when I swaps statements
Future.value(3).then(print);
Future.value(Future(() => 4)).then(print);
Future.microtask(() => 1).then(print);
Future.microtask(() => Future(() => 2)).then(print);
output is:
3
1
4
2
Questions:
Future.value
vs Future.microtask
?Future.value
completes first or Future.microtask
?4
and 2
) remains unchanged?Can someone explain this behavior considering event and microtask queue?
Future.microtask
schedules a microtask to execute the argument function. It then completes the future with the result of that function call.
Future()
and Future.delayed
schedules a timer task, the former with Duration.zero
, to execute a function, and complete the future with the result of that function call.
Future.value
takes a value, not a function to call. If you do Future.value(computation())
, the computation is performed (or at least started, in case it's async) right now.
If you do Future.microtask(computation)
, the computation is started in a later microtask.
In each case, if the function returns a future or the value passed to Future.value
is a future, then you'll also have to wait for that future to complete, before the future returned by the Future
constructor is completed with the same result.
For the concrete example:
Future.value(3).then(print);
This creates a future completed with the value 3
.
However, since futures promise to not call a callback, like then(print)
, immediately when the then
is called, it schedules a microtask to actually call the print
callback at a later time. So, you get an extra delay there.
In more detail:
Future.microtask(() => 1).then(print);
// This `Future.microtask(...)` creates future, call it F1,
// and schedules a microtask M1 to call `() => 1` later.
// Then adds callback C1 (`then(print)`) to F1, but F1 isn't completed yet,
// so nothing further happens.
Future.microtask(() => Future(() => 2)).then(print);
// Creates future F2 (`Future.microtask(...)`),
// schedules microtask M2 to run `() => Future(() => 2)` later,
// then callback C2 (`.then(print)`) to F2.
Future.value(3).then(print);
// Creates future F3 with value 3. Adds C3 (`then(print)`) to F3.
// Since F3 is complete, it schedules M3 to invoke C3.
Future.value(Future(() => 4)).then(print);
// Creates future F4 (`Future(() => 4)`)
// which starts *timer* T1 with duration zero to run `() => 4`.
// Then creates future F5 (`Future.value(...)`) with "value" F4.
// Completing with a future adds a callback C4 to F4,
// to notify F5 when a result is ready.
// Then adds callback C5 (`then(print)`) to F5.
That's what happens immediately. Then the event/microtask loop takes over.
() => 1
to the value 1.Future(() => 2)
.Future(...)
and a timer T2 with duration zero.() => 4
to 4.() => 2
and completes F6 with the value 2.So, three microtasks, two timers, and some future result propagation later, you get the result you see.
The second example can be done in the same way:
Future.value(3).then(print);
// Schedule a microtask to print 3.
Future.value(Future(() => 4)).then(print);
// Schedule a timer to (going through an extra future) print 4.
Future.microtask(() => 1).then(print);
// Schedule a microtask to compute and print 1.
Future.microtask(() => Future(() => 2)).then(print);
// Schedule a microtask to schedule a timer to eventually print 2.
The microtask-only ones, 3 and 1, should print first in order. Then it should print 4, and then 2, because the 2-timer is scheduled after the 4-timer. 3-1-4-2, which is what you see.