Search code examples
dartdart-async

What code is put on the Event Loop - the Future body or the call to execute upon its completion (then)?


I have a few basic questions about Dart Futures that I can't seem to get an answer to myself. Considering the following code:

Future(
  () => print('from Future') // 1
).then(
  (_) => print('after Future') // 2
);
  1. What is put on the Event Loop, code block 1 or 2?
  2. If 1 is put on the Event Loop, is 2 executed immediately after it, synchronously, or is it put on the Event Loop as well, for later execution?
  3. If 2 is executed immediately, would it ever make sense for 2 to be:
Future.delayed(someDuration, () => print('after Future'));

What would the use case be? Like to split a longer 'task' so that other code is run in between? Is it something that is actually done in practice, like in Flutter, to prevent 'jank'?

Edit: I found a very insightful article: https://webdev-angular3-dartlang-org.firebaseapp.com/articles/performance/event-loop#how-to-schedule-a-task, which kind of answers pretty much every single question I asked here.


Solution

  • The constructor you are calling is Future() which are documented as:

    Creates a future containing the result of calling computation asynchronously with Timer.run.

    If the result of executing computation throws, the returned future is completed with the error.

    If the returned value is itself a Future, completion of the created future will wait until the returned future completes, and will then complete with the same result.

    If a non-future value is returned, the returned future is completed with that value.

    https://api.dart.dev/stable/2.8.2/dart-async/Future/Future.html

    Where Timer.run is documented as:

    Runs the given callback asynchronously as soon as possible.

    This function is equivalent to new Timer(Duration.zero, callback).

    https://api.dart.dev/stable/2.8.2/dart-async/Timer/run.html

    So, since we are creating a timer which are already completed, it will immediately be put on the event loop.

    So with this knowledge we can answer your questions:

    What is put on the Event Loop, code block 1 or 2?

    Block 1 is put on the event loop. Since block 2 is dependent on the result from block 1, it will not be put on any queue. Instead, block 2 will be notified when block 1 has returned its result.

    If 1 is put on the Event Loop, is 2 executed immediately after it, synchronously, or is it put on the Event Loop as well, for later execution?

    As far as I understand the documentation, block 2 will be executed immediately synchronously as part of the block 1 is completed (unless the future as has already been completed which then will trigger a microtask):

    Register callbacks to be called when this future completes.

    When this future completes with a value, the onValue callback will be called with that value. If this future is already completed, the callback will not be called immediately, but will be scheduled in a later microtask.

    https://api.dart.dev/stable/2.8.2/dart-async/Future/then.html

    If 2 is executed immediately, would it ever make sense for 2 to be:

    The specific example does not make much sense. But yes, you can use the Future.delayed if you want to schedule smaller tasks on the event loop. It should be noted that Dart are single threaded so you cannot schedule tasks to be running in another thread by using Future.delayed.

    But in the context of Flutter, you properly want to have multiple smaller tasks so the UI can be drawn between each task. But if you are going to make some heavy calculations, you should properly use an Isolate to run these in another thread.