Repository: flutter
Issue: Consuming immediately available async results in one frame
Labels: framework,dependency: dart,c: proposal,P3,team-framework,triaged-framework

This issue is about the problem of consuming immediately available results from a `Future` within one frame.

A typical use case is a data API that caches results of previous requests.

Sometimes a result can be resolved synchronously (e.g. from a cache) or asynchronously (e.g. from the network). The producer of such a result can create and hand out a `Future` synchronously and asynchronously. Only async code can consume a `Future`, though. 

Code that runs as part of building widgets must be synchronous and cannot consume an immediately available result that is contained in a `Future` in the same frame that awaits the `Future`. The result is only accessible in the next frame, creating an unnecessary delay or worse a flicker in the UI.

# `SynchronousFuture`

`SynchronousFuture` calls its `then` callback synchronously, and thus allows its result to be consumed synchronously.

The name `SynchronousFuture` is somewhat distracting. We don’t necessarily care about the result being available synchronously. We care about the result being visible in the same frame.

## Problems with `SynchronousFuture`

- Breaks the `Future` contract. It happens to work with the current Dart `await` implementation, but that is not guaranteed.
- It should only be used internally and never be handed out to code that does not expect it, including the standard library. That makes it inherently hard to use and not a general solution.
    - “Use `SynchronousFuture` only when you're the only one ever calling `then` on it”
    - [https://github.com/flutter/flutter/issues/71554#issuecomment-737334219](https://github.com/flutter/flutter/issues/71554#issuecomment-737334219)
- Users are having issues because it does not work with some standard library APIs.
    - https://github.com/dart-lang/sdk/issues/50204

# Possible solutions

## Custom result type

A custom result type can expose an API to access the result as soon as it is available. The downside of this solution is that it excludes all the useful affordances available for working with async computations such as `await`, standard library APIs and packages that build on async Dart APIs.

## Multiple build/layout phases per frame

After building dirty widgets, allow microtasks that were scheduled to run, and build widgets that were dirtied by those microtasks. This needs to be repeated in a loop so that all widgets (including those that were rebuilt in another than the first build phase) can consume immediately available results.

In a loop:

- Build dirty elements
- Layout dirty render objects
- If microtask queue is empty
    - Break the loop
- Drain microtask queue

### Upsides

- Allows results that are immediately available to be used in the same frame.
- Does not require a change in Dart.
- Adheres to specified semantics of async Dart APIs.
- Composing async results with `await` and the standard library works as expected.
- No need for APIs to return `FutureOr` / other custom result type.

### Downsides

- Some widgets will be built twice in the same frame.
- Infinite loops can be induced accidentally.
    - build → schedule microtask → setState → build
- Enforcing that the build of one widget does not cause the build of a widget above it gets harder.

## Change Dart to somehow support synchronous `Future`s

- https://github.com/dart-lang/sdk/issues/50204
- https://github.com/dart-lang/language/issues/2033

As discussed in the two issues above, there are lots of problems with making that work.