-
Notifications
You must be signed in to change notification settings - Fork 1.7k
How can we make dart:async better for UIs? #33713
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
From a strictly AngularDart perspective, we could introduce a class or mixin that tries to encapsulate the hard parts of doing async work in a component: // Better name pending.
abstract class AsyncComponent implements OnDestroy {
/// Wraps a [future] to ensure it never completes if the component has been destroyed.
Future<T> whenActive<T>(Future<T> future);
} Example use: class Comp extends AsyncComponent implements OnInit {
@override
void ngOnInit() async {
final result = await whenActive(someRpcService());
// Will never make it here if the component has been destroyed.
}
} I imagine we'd have to write some sort of lint to make this very effective, but it is an idea. |
For Flutter: I, and I believe the rest of my team, have been happy with the Flutter has even built a custom future-like class to handle that issue with Futures. https://master-docs-flutter-io.firebaseapp.com/flutter/foundation/SynchronousFuture-class.html |
I think the underlying issue is very different, but related - One more thing I probably should mention is there still an issue of wasting resources, even if we use the wrapper approach or Imagine downloading a 50Mb video from the server, and the user navigates away, on mobile... |
State.isMountedFlutter has to work around some of the same issues regarding async work that Angular dart does. For starters, we have class MyWidgetState extends State<MyWidget> {
String _name;
Future<void> doSomeWork() async {
final String name = await Api.getName();
// might have been dismounted, check before calling `setState`
if (isMounted) {
setState(() { _name = name });
}
}
@override
void dispose() {
// only chance to clean up resources.
super.dispose();
}
Widget build(BuildContext context) => ...
}
FutureBuilderThere are some tangentially related issues with a widget called FutureBuilder. This takes a Future and a builder closure and rebuilds whenever the Future changes. However, recently I've been seeing several issues where it is used incorrectly, but in a non-obvious way. In the example below, instead of passing a Future that is cached on a State object, I just directly pass the invocation of an async method to my Future<String> _getName() async { ... }
Widget build(BuildContext context) {
new FutureBuilder<String>(
future: _calculation(), // a Future<String> or null
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none: return new Text('Press button to start');
case ConnectionState.waiting: return new Text('Awaiting result...');
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return new Text('Result: ${snapshot.data}');
}
},
)
} Is Future a value or a Computation?A future has value-like semantics, in that it is immutable and un-cancellable. But it is also an eager computation, and naturally full of side effects. And the combination of these two features means that we can't, as framework authors, solve either problem above. Introducing our own classes like |
Good point, one of the issues is that the "best" syntax ( |
As someone who is making an application that may have impatient users, having a way to handle this would be great. How would one work around this until a fix comes out? |
It's true that We have The issue here seems to be a lack of easy state management and sequencing. There is nothing inherently wrong with the original example except that the The Is the answer here perhaps for There is still some room for language fiddling around this, including If we ever get automatic resource disposal (like C#'s We could also make other objects than |
No plans to fix this in the platform libraries. It's a domain specific issue with state management. |
Given Dart's new focus on being the best client/UI language, I thought I'd revisit this.
Internally there is a long-ish thread with customers having issues, primarily around
Future
andasync/await
, and UI components that are shorter-lived than the pending operations encapsulated by theFuture
.An internal user writes:
/cc @lrhn who already jumped into this conversation, and pointed out issues with it :)
This seems to affect both AngularDart and Flutter, though in different ways.
AngularDart
A basic pattern is something like below:
See the bug?
state.update(result)
might happen afterstate.dispose()
.Users need to either:
Ensure all stateful objects implement some sort of state machine to track validitity
Stop using
async/await
Use something like
CanceleableOperation
frompacakge:async
:ReactJS also has/had this problem, and suggested wrapping all ES6 promises with
makeCancelable
- fairly close to theCancelableOpreation
code above (though less wordy):Flutter
Flutter tries to encapsulate many of the issues with
FutureBuilder
. From what I've hear from both framework authors and customers, there is several holes with this approach, as well. I'll let them fill in the details.The text was updated successfully, but these errors were encountered: