我开始对 Dart 感到非常沮丧
async
...
我想要的只是延迟加载一些值并将其分配给我的类中的属性:
Future<String> getMeSomeBar() async => "bar";
class Foo {
late String bar = await getMeSomeBar();
}
这当然行不通。
runBlocking
函数,专为类似的情况而设计,但似乎没有一个 Dart 等效项或模式可以让我做同样的事情.
至少到目前为止我还没有找到。
我什至无法在 Dart 中执行基本的自旋锁,因为无法检查
Future
的状态...
因此,因为
getMeSomeBar
超出了我的控制范围,我需要将Foo.bar
制作为Future<String>
。
这意味着我必须将任何访问
Foo.bar
的函数设为 async
函数。
这意味着我必须使任何函数访问 that 函数,一个
async
函数。
这意味着我也必须让任何函数访问那些函数
async
。
这意味着...
你明白了。最后,整个该死的应用程序将是异步的,因为我似乎完全没有办法阻止线程/隔离,直到
Future
完成。
这不能解决问题吗?!
有办法避免这种情况吗?
(不,使用
then
没有帮助,因为那样你要么等待 that Future
的完成 - 同样,你似乎只能在 async
函数中完成 - 或者你正在赌博你的Future
s'会“及时”返回 - 这简直是愚蠢的。)
不确定这对您来说是否是一个可扩展的解决方案,但您可以延迟构建
Foo
类,直到完成 Future<T>
,从而有效地创建 Future<Foo>
的异步工厂。
然后,您可以 (1) 在构建实际布局之前在
Future<Foo>
小部件中使用该 FutureBuilder
来侦听 Future<Foo>
进行解析,或者 (2) 您可以在有状态小部件中使用您自己的 Completer<Foo>
进行更新完成后的状态。
另外:如果您必须等待多个 Future 来构建
Foo
,您可以在您的 Future.all(...)
Future-factory 中使用 Foo.fromAsyncCall
。
这是一个例子:
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
// home: Example1(),
home: Example2(),
);
}
}
// a standin for some object
Future<String> getMeSomeBar() async {
// just an arbitrary delay
await Future.delayed(const Duration(seconds: 3));
return 'bar';
}
// a standin for some class
class Foo1 {
final String bar;
const Foo1(this.bar);
// You can create a function that will build your object upon the completion
// of a future
static Future<Foo1> fromAsyncCall([dynamic someArgs]) => Future(() async {
// you can access someArgs in here, if you need to
String bar = await getMeSomeBar();
return Foo1(bar);
});
}
// EX.1 - You could try to use a FutureBuilder listening for the completion of
// the future
class Example1 extends StatefulWidget {
const Example1({super.key});
@override
State<Example1> createState() => _Example1State();
}
class _Example1State extends State<Example1> {
late Future<Foo1> foo1;
@override
void initState() {
super.initState();
foo1 = Foo1.fromAsyncCall();
}
@override
void dispose() {
foo1.ignore();
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: FutureBuilder(
future: foo1,
builder: (BuildContext context, AsyncSnapshot<Foo1> snapshot) {
if (snapshot.hasData) {
return Text(
snapshot.data!.bar,
style: TextStyle(
color: Colors.green,
),
);
}
if (snapshot.hasError) {
return Text(
snapshot.error?.toString() ?? 'Error',
style: TextStyle(
color: Colors.red,
),
);
}
return CircularProgressIndicator.adaptive();
},
),
),
);
}
// EX.2 - You could do it yourself by listening for a Completer. Unfortunately,
// the future value that the completer wraps is a Future<T> so you can't
// directly access the future's inner value/error here - you'd have the same
// problem
class Example2 extends StatefulWidget {
const Example2({super.key});
@override
State<Example2> createState() => _Example2State();
}
class _Example2State extends State<Example2> {
final Completer<void> _completer = Completer();
Foo1? _value;
Object? _error;
@override
void initState() {
super.initState();
Foo1.fromAsyncCall().then((Foo1 value) {
if (_completer.isCompleted || !mounted) return;
setState(() {
_completer.complete();
_value = value;
});
}).catchError((Object error, StackTrace _) {
if (_completer.isCompleted || !mounted) return;
setState(() {
_completer.completeError(error);
_error = error;
});
});
}
@override
Widget build(BuildContext context) {
Widget? child;
if (_completer.isCompleted) {
if (_value != null) {
child = Text(
_value!.bar,
style: TextStyle(
color: Colors.green,
),
);
}
if (_error != null) {
child = Text(
_error.toString(),
style: TextStyle(
color: Colors.red,
),
);
}
}
child ??= CircularProgressIndicator.adaptive();
return Scaffold(
body: Center(
child: child,
),
);
}
}