提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
一、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本质上就是协程的一种语法糖。协程,也叫作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;
}
}
Flutter中的异步方式_flutter 异步_九心说的博客-CSDN博客
Futrue 是 Dart 异步编程的核心之一,表示一个不会立即返回的结果。
一般这么使用:
// 模拟网络请求
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 方法中,我们可以处理捕获的异常。
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));
}
除了上述的 Future.delayed方法,还有一些 factory 方法:
方法 | 介绍 |
Future(FutureOr computation()) | 将 Future 放入 event queue |
Future.microtask | 将 Future 放入 microtask queue |
Future.sync | 立刻执行 Future 里面的完成代码 |
上述的方法主要影响的是 Future 完成的时机。
通过 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
Future 是异步的一个核心,用来表示一个异步事件。
Stream 则是异步的另外一个核心,用来表示一系列异步事件。
创建 Stream 一般有两种方式:使用 yield 和 StreamController。
使用 yield 方式比较简单,比如我发送十次请求结果:
Stream
createStream() async* {
for(int i = 0; i < 10; i++) {
String result = await requestNetwork("num: $i");
yield result;
}
}
注意,方法右边使用的 async* 关键字,而不是 async,请求的结果使用 yield 发送。
StreamController 的功能更加强一点:
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