Flutter&Dart-异步编程Future、Stream极速入门


Dart是单线程模型,我们写的代码都运行在同一个线程中。如果做了耗时操作,会使应用程序阻塞。Dart中使用Future和Stream编写异步任务。

Future

Future是一个不会马上完成的计算过程,说的通俗一点就是一个异步执行过程,需要配合async和await一起使用。不会阻塞在此之后的代码,等待计算完成后才会返回结果。

类似于JavaScript中的Promiseasyncawait

用法

void main()  {
  requestApi();
  doSomething1();
  doSomething2();
}

doSomething1() {
  print("doSomething1");
}

doSomething2() {
  print("doSomething2");
}

/// 异步请求数据
Future requestApi() async {
  ....
  return 
}

由于requestApi() 是个异步函数,程序运行后,不会等待函数执行完成,下面的代码也会立即开始执行。

等待返回结果

如果想在异步函数返回结果后再做其他操作,可以使用then()方法来监听。

void main()  {
  requestApi().then((value) {
    /// 结果返回,开始处理
    showApiData(value);
  });
  print("程序开始运行...");
}

............
  
showApiData(dynamic value){
  print("展示获取的数据: $value");
}

FutureBuilder

const FutureBuilder({
    Key? key,
    this.future,
    this.initialData,
    required this.builder,
})

FutureBuilder会基于传入的future的返回结果来构建Widget。

使用示例

一般用于网络请求后更新UI。
在这里插入图片描述

class FutureBuilderDemo extends StatefulWidget {
  const FutureBuilderDemo({Key key}) : super(key: key);

  @override
  _FutureBuilderDemoState createState() => _FutureBuilderDemoState();
}

class _FutureBuilderDemoState extends State<FutureBuilderDemo> {
  Future<String> futureData;

  @override
  void initState() {
    super.initState();
    /// ui初始化时开始网络请求数据
    futureData = getData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('FutureBuilder 测试'),
      ),
      body: Container(
        child: Center(
          child: FutureBuilder<String>(
              future: futureData,
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return Text("获取的数据==${snapshot.data}");
                } else if (snapshot.hasError) {
                  return Icon(Icons.error_outline);
                } else {
                  return CircularProgressIndicator();
                }
              }),
        ),
      ),
    );
  }

  Future<String> getData() async {
    /// 模拟网络请求,等待5秒返回结果
    await Future.delayed(Duration(seconds: 5));

    /// 返回一个错误
    // return Future.error("error");
    /// 返回一个成功的结果
    return "Hello,world";
  }
}

Stream

Stream是一系列异步事件的序列,相当于一个异步的跌代器(Iterable)。不同于Future只是一个事件执行过程,Stream可以发送执行多个事件。使用场景不同。

用法

创建方式有2种,一个是单订阅的Stream,一种是能多订阅的流,也就是可以广播。

订阅后(listen)会返回一个StreamSubscription,就是一个观察者。

2种方式的构造方法都有一个可选参数sync,默认为false。

..........
   bool sync = false}) {
return sync
        ? _SyncStreamController<T>(....)
        : _AsyncStreamController<T>(...);

通过源码可以看出,sync为false创建的是一个异步事件StreamController, sync为true时创建的是一个同步StreamController。因为大部分场景下使用Stream都是为了异步,所以我们直接不传入即可。

单订阅

/// 直接使用构造函数
StreamController<String> _controller = StreamController();
StreamSubscription subscription = _controller.stream.listen((event) {
    print("subscription 接收到 $event");
});

广播订阅

void main() {
  /// 使用工厂方法构造可广播的Controller
  StreamController _controller = StreamController.broadcast();

  /// 使用多个观察者订阅同一个Stream.
  StreamSubscription subscription1 =
      _controller.stream.listen((event) => print("sub1 接收到 $event"));
  StreamSubscription subscription2 =
      _controller.stream.listen((event) => print("sub2 接收到 $event"));
  StreamSubscription subscription3 =
      _controller.stream.listen((event) => print("sub3 接收到 $event"));

  /// 发送事件后,所有已订阅的观察者的都能接收到事件。
  _controller.add("Hello");
}

运行多次,打印结果都如下不变:

sub1 接收到 Hello
sub2 接收到 Hello
sub3 接收到 Hello

Process finished with exit code 0

结论:

事件订阅(listen)的越早,接收到事件的优先级越高。

释放资源

使用完事件后或者在Flutter中Widget关闭时,记得也同时关闭Stream。

_controller.stream.listen((event) {}, onDone: () => print("收到onDone事件"));

_controller.close();
subscription.cancel();

关闭后会自动触发onDone回调方法。

StreamBuilder

在界面中,一般使用StreamBuilder来来配合Stream使用。可以实现多状态界面。

使用示例

在这里插入图片描述

/// 定义3种ui状态
enum UIState { type_1, type_2, type_3 }

class StreamBuilderDemo extends StatefulWidget {
  const StreamBuilderDemo({Key key}) : super(key: key);

  @override
  _StreamBuilderDemoState createState() => _StreamBuilderDemoState();
}

class _StreamBuilderDemoState extends State<StreamBuilderDemo> {
  StreamController<UIState> _controller;

  @override
  void initState() {
    super.initState();

    /// 初始化controller
    _controller = StreamController();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('StreamBuilder测试'),
      ),
      body: Container(
        // height: double.infinity,
        child: Center(
          child: StreamBuilder<UIState>(

              /// 传入stream
              stream: _controller.stream,
              builder: (BuildContext context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  /// hasData代表有接收到事件
                  var data = snapshot.data;
                  Widget widget;

                  ///根据返回不回的类型,展示不同的图片
                  switch (data) {
                    case UIState.type_1:
                      widget = Icon(Icons.timer, size: 100);
                      break;
                    case UIState.type_2:
                      widget = Icon(Icons.done, size: 100);
                      break;
                    case UIState.type_3:
                      widget = Icon(Icons.ac_unit, size: 100);
                      break;
                  }
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      widget,
                      Text("$data"),
                    ],
                  );
                } else if (snapshot.hasError) {
                  /// 接收到错误事件
                  return Icon(Icons.error_outline_rounded, size: 100);
                } else {
                  /// 什么都没有,代表没还有接收到事件
                  return Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      CircularProgressIndicator(),
                      Text("初始化中,还没接收到收到状态"),
                    ],
                  );
                }
              }),
        ),
      ),
      floatingActionButton: FloatingActionButton(
          onPressed: () => generateState(), child: Icon(Icons.add)),
    );
  }

  /// 随机生成不同的状态,发送事件
  generateState() {
    var randomIndex = Random().nextInt(UIState.values.length);
    _controller.add(UIState.values[randomIndex]);
  }

  @override
  void dispose() {
    super.dispose();

    /// 回收资源
    _controller.close();
  }
}

猜你喜欢

转载自blog.csdn.net/adojayfan/article/details/120697047