dart-pad
dart-pad copied to clipboard
SynchronousFuture behavior is different depending on the SDK
The behavior of a SynchronousFuture is different dependeing on whether the Dart SDK or Flutter SDK is used. The Dart SDK executes immediately, but the Flutter SDK does not:
Have you tested these on the VMs?
Another reason they might differ is that in the Flutter case we wrap main() in a bunch of Zone magic.
It looks like this only happens when a Flutter app is compiled to JS:
| Platform | sync | async |
|---|---|---|
| Web (Dart) | X | |
| Web (Flutter) | X | |
| VM (Dart) | X | |
| VM (Flutter) | X |
FYI @yjbanov
Update: this is only happening with DDC. (DartPad uses DDC for Flutter code)
I'm surprised that we're able to subvert the asynchrony of await on any platform...
@lrhn - were you aware of this behavior?
It's unsurprising that things break when you implement Future incorrectly. Since await is defined in terms of calling then, and then is specified as not calling the callback during the then call, weird things happen if you do so anyway. We don't (necessarily) add extra overhead to try to avoid implementations of Future which are broken.
We also don't try to keep our implementations bug-compatible. If your Future doesn't behave like a proper future, we can go off the rails in different ways on different platforms.
Basically, it's unspecified behavior. If it hurts, stop doing it.
Nothing prevents you from creating a Stream which calls its event callbacks after you've called cancel, or after sending a done event, or sending more than one done event, or creating a future which calls the then callback more than once, or both the data and error callbacks. It breaks assumptions that other code might be based on, and the behavior of the platform is undefined if you do so. Same if your Future calls the callback in during the call to then.
(Also, the specification of await doesn't say when execution continues:
The current invocation of the function body immediately enclosing $a$ is suspended until after $f$ completes. At some time after $f$ is completed, control returns to the current invocation.
so, it's not wrong to be asynchronous, no matter when the future calls then. We actually do try to make it continue within the same microtask queue, but that is it.)
Related to https://github.com/flutter/flutter/issues/82827