阻塞等待异步函数的未来

问题描述 投票:0回答:1

我开始对 Dart 感到非常沮丧

async
...

我想要的只是延迟加载一些值并将其分配给我的类中的属性:

Future<String> getMeSomeBar() async => "bar";
class Foo {
  late String bar = await getMeSomeBar();
}

这当然行不通。

但真正让我发疯的是,我知道 Kotlin 有一个

runBlocking
函数,专为类似的情况而设计,但似乎没有一个 Dart 等效项或模式可以让我做同样的事情.

至少到目前为止我还没有找到。

我什至无法在 Dart 中执行基本的自旋锁,因为无法检查

Future
的状态...

因此,因为

getMeSomeBar
超出了我的控制范围,我需要将
Foo.bar
制作为
Future<String>

这意味着我必须将任何访问

Foo.bar
的函数设为
async
函数。

这意味着我必须使任何函数访问 that 函数,一个

async
函数。

这意味着我也必须让任何函数访问那些函数

async

这意味着...

你明白了。最后,整个该死的应用程序将是异步的,因为我似乎完全没有办法阻止线程/隔离,直到

Future
完成。

这不能解决问题吗?!

有办法避免这种情况吗?

(不,使用

then
没有帮助,因为那样你要么等待 that
Future
的完成 - 同样,你似乎只能在
async
函数中完成 - 或者你正在赌博你的
Future
s'会“及时”返回 - 这简直是愚蠢的。)

flutter dart asynchronous async-await future
1个回答
0
投票

不确定这对您来说是否是一个可扩展的解决方案,但您可以延迟构建

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,
      ),
    );
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.