Flutter随记:异步多线程使用实现

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

 

目录

前言

一、async、await

二、Future

1. 使用介绍

2. 处理多个请求

3. 一些通用 Api

4. FutureBuilder



前言

参考链接:

Flutter中的异步方式_flutter 异步_九心说的博客-CSDN博客

flutter async和await原理解析_flutter await_一叶飘舟的博客-CSDN博客

在异步调用中有三个关键词,async、await、Future,其中async和await需要一起使用。在Dart中可以通过async和await进行异步操作,async表示开启一个异步操作,也可以返回一个Future结果。如果没有返回值,则默认返回一个返回值为null的Future。

await的操作,不会影响方法外后续代码的执行;只会阻塞async方法的后续代码


一、async、await

async、await的操作属于"假异步",这是为什么呢?
如果想要得到这个问题的答案,首先我们需要了解async、await的原理,了解协程的概念,因为async、await本质上就是协程的一种语法糖。协程,也叫作coroutine,是一种比线程更小的单元。如果从单元大小来说,基本可以理解为 进程->线程->协程

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

  @override
  State createState() =>
      _AsyncAndAwaitAsynchronousOperationState();
}

class _AsyncAndAwaitAsynchronousOperationState
    extends State {
  @override
  void initState() {
    // TODO: implement initState

    super.initState();
  }

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('单线程', style: TextStyle(color: Colors.amber))),
      floatingActionButton: TextButton(
        onPressed: () {
          Navigator.pop(context, 'jieshu');
        },
        child: Text('返回上一页'),
      ),
      body: Container(
        child: TextButton(
          onPressed: () {
            _testAsyncKeyword();
          },
          child: Text('执行一个async函数'),
        ),
      ),
    );
  }

/*
执行结果:
flutter: _testAsyncKeyword函数开始了:2023-06-28 22:36:12.030786
flutter: _testString函数开始了:2023-06-28 22:36:12.034836
flutter: _testAsyncKeyword函数结束了:2023-06-28 22:36:12.035255
flutter: _testString函数结束了:2023-06-28 22:36:12.338224
flutter: 我是测试字符串===1

在代码示例中,_testAsyncKeyword(){}执行到_testString()async{}方法,会同步进入方法内部进行执行,
当执行到await时就会停止_testString()async{}内部的执行,跳出方法去继续执行外面_testAsyncKeyword()中后续的代码。
所以await的操作,不会影响_testAsyncKeyword()后面代码的执行("t_testAsyncKeyword函数结束了"会晚于“_testString函数开始了”打印,)。
当await有返回后,会继续从await的位置继续执行(所以先打印出了 "_testString函数结束了"打印 ,然后返回Future的结果,并通过print打印出 "我是测试字符串===1")。


若_testAsyncKeyword() async{}  是async异步方法:
执行结果:
flutter: _testAsyncKeyword函数开始了:2023-06-28 22:55:13.578807
flutter: _testString函数开始了:2023-06-28 22:55:13.581601
flutter: _testString函数结束了:2023-06-28 22:55:13.884072
flutter: 我是测试字符串===1
flutter: _testAsyncKeyword函数结束了:2023-06-28 22:55:13.885059


_testAsyncKeyword()async{}执行到 await _testString()async{}方法,会同步进入方法内部进行执行,
当执行完testString()async{}内部的代码并有返回后,会继续从await _testString()的位置继续执行后面的代码
(所以先打印结果如上)。


*/


  // _testAsyncKeyword() {
  //   print("_testAsyncKeyword函数开始了:${DateTime.now()}");
  //   _testString().then((value) => print(value));
  //   print("_testAsyncKeyword函数结束了:${DateTime.now()}");
  // }
  _testAsyncKeyword() async {
    print("_testAsyncKeyword函数开始了:${DateTime.now()}");
    await _testString().then((value) => print(value));
    print("_testAsyncKeyword函数结束了:${DateTime.now()}");
  }

  Future _testString() async {
    print("_testString函数开始了:${DateTime.now()}");
    Future f = Future.delayed(Duration(milliseconds: 300), () {
      return "我是测试字符串===1";
    });
    String result = await f;
    print("_testString函数结束了:${DateTime.now()}");
    return result;
  }

}

二、Future

Flutter中的异步方式_flutter 异步_九心说的博客-CSDN博客
Futrue 是 Dart 异步编程的核心之一,表示一个不会立即返回的结果。

1. 使用介绍


一般这么使用:

// 模拟网络请求
Future requestNetwork(String name){
  return Future.delayed(Duration(seconds: 1), (){
    return "Hello, i am $name";
  });
}

void doSomeThing() {
  requestNetwork("JiuXin")
      .then((value) => print(value))
      .catchError((error, stackTrace) => print(stackTrace));
}


我们用 Future.delayed 延时模拟网络请求。

上面的代码使用了链式调用,在 then 方法中,我们可以获取异步返回的结果,在 onError 方法中,我们可以处理捕获的异常。

2. 处理多个请求


then 方法是这样的:

Future then(FutureOr onValue(T value), {Function? onError})


意味处理多个连续请求,我们可以使用 then 避免陷入回调地狱:

// 模拟网络请求
Future requestNetwork(String name){
  return Future.delayed(Duration(seconds: 1), (){
    return "Hello, i am $name";
  });
}

// 模拟数据库操作
Future saveInDB(String name) {
  return Future.delayed(Duration(seconds: 1), (){
    // 处理数据库操作
    return "save in DB success, Hello, i am $name";
  });
}

void doSomeThing() {
  requestNetwork("JiuXin")
      .then((value) => saveInDB(value))
      .then((value) => print(value))
      .catchError((error, stackTrace) => print(stackTrace));
}


3. 一些通用 Api


除了上述的 Future.delayed方法,还有一些 factory 方法:

                                                 

方法 介绍
Future(FutureOr computation()) 将 Future 放入 event queue
Future.microtask   将 Future 放入 microtask queue
Future.sync  立刻执行 Future 里面的完成代码

 
上述的方法主要影响的是 Future 完成的时机。

4. FutureBuilder


通过 FutureBuilder,我们可以在 StatelessWidget 展现出不同的状态,还是上面的代码:

class TestPage extends StatelessWidget {
  const TestPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("TestPage"),
      ),
      body: Center(
        child: FutureBuilder(
          future: requestNetwork("JiuXin").then((value) => saveInDB(value)),
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              if (snapshot.hasError) {
                return Text("onError: ${snapshot.error}");
              } else {
                return Text(snapshot.data ?? "");
              }
            } else {
              return CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}


对于 Future 来说,我们只要关注 Future 有没有完成,主要关心三个状态:

ConnectionState.done 成功:Future 正常完成
ConnectionState.done 错误:错误态
非完成态
————————————————
版权声明:本文为CSDN博主「九心说」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35063091/article/details/127356382

三、Stream

Future 是异步的一个核心,用来表示一个异步事件。

Stream 则是异步的另外一个核心,用来表示一系列异步事件。

1. 创建 Stream


创建 Stream 一般有两种方式:使用 yield 和 StreamController。

1.1 yield

使用 yield 方式比较简单,比如我发送十次请求结果:

Stream createStream() async* {
  for(int i = 0; i < 10; i++) {
    String result = await requestNetwork("num: $i");
    yield result;
  }
}
注意,方法右边使用的 async* 关键字,而不是 async,请求的结果使用 yield 发送。

1.2 StreamController

StreamController 的功能更加强一点:

Stream使用更加灵活
可以缓存发射的数据
结构如图:

Stream图片
————————————————

主要分为四个角色:

StreamController:控制整个 Stream 流程

Stream:数据源,可被监听,Single-Subscription 只能被监听一次,Broadcast Stream 可以被多次监听

StreamSink:用来添加数据的地方

StreamSubscription:监听 Stream 生成的对象,可取消

StreamController 使用流程如下,参考 EventBus:
 

class EventBus {
  StreamController _streamController;
  StreamController get streamController => _streamController;
  StreamSink get _streamSink => _streamController.sink;

  // 如果想使用Single-Subscription,_streamController = StreamController()
  // 如果想使用BroadCast,StreamController.broadcast(sync: sync)
  EventBus({bool sync = false})
      : _streamController = StreamController.broadcast(sync: sync);

  EventBus.customController(StreamController controller)
      : _streamController = controller;

  Stream on() {
    if (T == dynamic) {
      return streamController.stream as Stream;
    } else {
      return streamController.stream.where((event) => event is T).cast();
    }
  }

  void fire(event) {
    _streamSink.add(event);
  }

  void destroy() {
    _streamController.close();
  }
}

使用处:

EventBus bus = EventBus(sync: true);

class RequestEvent{
  String content;

  RequestEvent({required this.content});
}

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

  @override
  State createState() => _StatePageState();
}

class _StatePageState extends State {

  String str = "JiuXin";
  late StreamSubscription _subscription;

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

    _subscription = bus.on().listen((event) {
      setState(() {
        str = event.content;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text(str),
    );
  }

  @override
  void dispose() {
    super.dispose();
    _subscription.cancel();
  }
}

在我们想要的调用点使用 bus.fire(RequestEvent("content")) 即可,需要注意的是,在 StatefulWidget 的 dispose 周期中,我们需要取消对应的监听。

Stream 跟 Rx 系列很相似,并且也有很多其他方便的 api 供我们调用,感兴趣的可以自己看一下。

2. StreamBuilder使用
StreamBuilder 和 FutureBuilder 有点像,又有点不像。

从整个使用流程来说,它们是一样的,对于状态的监听来说,它们是不一致的。

对于上面的 EventBus,同一个页面,如果我们想在 StreamBuilder 中使用:

class PageOne extends StatelessWidget {

  PageOne({Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: StreamBuilder(
        stream: bus.on(),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return Text("onError: ${snapshot.error}");
          }
          switch (snapshot.connectionState) {
            case ConnectionState.none:
              return Text("暂时没有数据哦~");
            case ConnectionState.waiting:
              return CircularProgressIndicator();
            case ConnectionState.active:
              return Text('${snapshot.data?.content ?? ""}');
            case ConnectionState.done:
              return Text('Stream 已关闭');
          }
        },
      ),
    );
  }
}

解释一下:

ConnectionState.active:Event 发送成功

ConnectionState.done:当 StreamSink 关闭后

FutureBuilder 是在 ConnectionState.done 以后接受数据。

总结
Dart 中的异步方式还是挺简单的,如果有疑惑,我们评论区见。

版权声明:本文为CSDN博主「九心说」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_35063091/article/details/127356382

你可能感兴趣的:(flutter)