异步函数概念:
Dart类库有非常多的返回Future或者Stream对象的函数。 这些函数被称为异步函数
- 一个Future只会对应一个结果,要么成功,要么失败。
- 在Dart中Stream 也是用于接收异步事件数据,和Future 不同的是,它可以接收多个异步操作的结果,它常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写、倒计时等。
Completer
用Future来处理一个异步操作,dart:async提供了Completer类,通过实例这个类生成Future,同时在用这个实例去控制生成的future的成功或者失败的状态。
1.类似javaScript的promise的参数函数的参数提供了resolve,reject方法去操控状态,completer.complete就相当于promise的resolve,completer.catchError相当于promise中的reject()
2.使用场景:一般如果你的异步操作是带回调的不能即时返回结果的类型
3.参考:flutter之Completer_yzpyzp的博客-CSDN博客
代码示例如下:
Future openImagePicker () {
Completer completer = new Completer();
// ImagePicker 是一个图片选择插件
ImagePicker.singlePicker(
context,
singleCallback: (data) {
completer.complete(data);
},
failCallback:(err) {
completer.catchError(err);
}
);
return completer.future;
}
// 使用
openImagePicker().then((data) {}).catchError((err){});
StreamController.broadcast()和StreamController()的区别
StreamController(),只能有一个监听,会缓存数据
StreamController.broadcast():可以有多个监听,不会缓存数据
StreamController _streamController = StreamController.broadcast();
@override
void dispose() {
_streamController.close();
super.dispose();
}
@override
void initState() {
Future.delayed(Duration(seconds: 10), () {
_streamController.stream.listen(
(event) {
MyLogUtil.d('stream:$event');
},
onError: (error) => MyLogUtil.d('ERROR:$error'),
onDone: () => MyLogUtil.d('DONE'),
);
});
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("stream的使用"),
),
body: Center(
child: Column(
children: [
ElevatedButton(
onPressed: () => _streamController.sink.add(10),
child: Text("10")),
ElevatedButton(
onPressed: () => _streamController.sink.add('hi'),
child: Text("hi")),
ElevatedButton(
onPressed: () => _streamController.sink.addError('error'),
child: Text("error")),
ElevatedButton(
onPressed: () => _streamController.sink.close(),
child: Text("close")),
StreamBuilder(
stream: _streamController.stream
.where((event) => event > 5) //添加条件判断
.map((event) => event * 2) //
.distinct(), //去重,比如上面重复点击按钮,只会builder一次
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Text('NONE:没有数据流');
break;
case ConnectionState.waiting:
return Text('WAITING:等待数据流');
break;
case ConnectionState.active:
if (snapshot.hasError) {
return Text('ACTIVE:错误:${snapshot.error}');
} else {
return Text('ACTIVE:正常:${snapshot.data}');
}
break;
case ConnectionState.done:
return Text('done:数据流关闭');
break;
}
return Container();
},
),
],
),
),
);
}
常用的属性:(where、map、distinct)
stream: _streamController.stream
.where((event) => event > 5)//添加条件判断
.map((event) => event * 2)//
.distinct(),//去重,比如上面重复点击按钮,只会builder一次
异步数据更新UI:
StreamBuilder是用于配合Stream来展示流上事件(数据)变化的UI组件,凡是UI会依赖多个异步数据而发生变化的场景都可以使用StreamBuilder
FutureBuilder展示异步任务状态,会依赖一个Future,它会根据所依赖的Future的状态来动态构建自身
async await
await的操作,不会影响方法外后续代码的执行;只会阻塞async方法内的后续代码
async、await原理:之所以说async/await是假异步,是因为他在执行过程中并没有开启新的线程更没有并发执行,而是通过单线程上的任务调度(协程,没有并发执行功能)实现的:
当代码执行到async则表示进入一个协程,会同步执行async的代码块。async的代码块本质上也相当于一个函数,并且有自己的上下文环境。当执行到await时,则表示有任务需要等待,CPU则去调度执行其他IO,也就是后面的代码或其他协程代码。过一段时间CPU就会轮询一次,看某个协程是否任务已经处理完成,有返回结果可以被继续执行,如果可以被继续执行的话,则会沿着上次离开时指针指向的位置继续执行,也就是await标志的位置。
由于并没有开启新的线程,只是进行IO中断改变CPU调度,所以网络请求这样的异步操作可以使用async、await,但如果是执行大量耗时同步操作的话,应该使用isolate开辟新的线程去执行。
例子1
_testAsyncKeyword() {
print("test函数开始了:${DateTime.now()}");
_testString().then((value) => print(value));
print("test函数结束了:${DateTime.now()}");
}
Future<String> _testString() async {
Future f = Future.delayed(Duration(milliseconds: 300), () {
return "我是测试字符串===1";
});
String result = await f;
print("我是测试字符串===2");
return result;
}
// flutter: test函数开始了:2021-05-27 13:58:56.595964
// flutter: test函数结束了:2021-05-27 13:58:56.598801
// flutter: 我是测试字符串===2
// flutter: 我是测试字符串===1
在代码示例中,执行到_testString()方法,会同步进入方法内部进行执行,当执行到await时就会停止async内部的执行,从而继续执行外面的代码。所以await的操作,不会影响后面代码的执行("test函数结束了"会先于_testString()内部打印)。
当await有返回后,会继续从await的位置继续执行(所以先打印出了 "我是测试字符串===2" ,然后返回Future的结果,并通过print打印出 "我是测试字符串===1")。
例子2
_testAsyncKeyword() async {
print("test函数开始了:${DateTime.now()}");
print(await _testString());
print("test函数结束了:${DateTime.now()}");
}
Future<String> _testString() async {
Future f = Future.delayed(Duration(milliseconds: 300), () {
return "我是测试字符串===1";
});
String result = await f;
print("我是测试字符串===2");
return result;
}
// flutter: test函数开始了:2021-05-27 14:06:48.185704
// flutter: 我是测试字符串===2
// flutter: 我是测试字符串===1
// flutter: test函数结束了:2021-05-27 14:06:48.497481
在代码示例中, _testAsyncKeyword() 本身内部就有一个await操作,当执行到await时就会停止_testAsyncKeyword() async内部的执行.等待_testString()有结果返回之后,继续执行.
_testString()内部也是有一个await操作,当执行到await时就会停止_testString() async内部的执行,等待300毫秒,Future有结果后,打印字符串2
_testAsyncKeyword() 继续执行 打印 字符串1 及 结束
例子3
_testAsyncKeyword() {
print("test函数开始了:${DateTime.now()}");
firstString().then((value) => print(value));
secondString().then((value) => print(value));
thirdString().then((value) => print(value));
print("test函数结束了:${DateTime.now()}");
}
_testKeyword2() async{
print("test函数开始了:${DateTime.now()}");
print(await firstString());
print(await secondString());
print(await thirdString());
print("test函数结束了:${DateTime.now()}");
}
Future<String> firstString() {
return Future.delayed(Duration(milliseconds: 300), () {
return "我是一个字符串";
});
}
Future<String> secondString() {
return Future.delayed(Duration(milliseconds: 200), () {
return "我是二个字符串";
});
}
Future<String> thirdString() {
return Future.delayed(Duration(milliseconds: 100), () {
return "我是三个字符串";
});
}
//_testAsyncKeyword() 的打印:
//flutter: test函数开始了:2021-05-27 14:17:42.620409
//flutter: test函数结束了:2021-05-27 14:17:42.624359
//flutter: 我是三个字符串
//flutter: 我是二个字符串
//flutter: 我是一个字符串
//_testKeyword2() 的打印:
//flutter: test函数开始了:2021-05-27 14:18:41.401992
//flutter: 我是一个字符串
//flutter: 我是二个字符串
//flutter: 我是三个字符串
//flutter: test函数结束了:2021-05-27 14:18:42.027575
参考:flutter async和await原理解析(await阻塞当前方法,不阻塞方法外的代码继续执行)_flutter await-CSDN博客
Dart中async和async*的区别:
==================Dart中async和async*的区别start============================
async和await
这两个关键字的使用只需要记住两点:
只有async方法才能使用await关键字调用方法
如果调用别的async方法必须使用await关键字
async是让方法变成异步。
await是等待异步方法执行完成
Dart中async和async*有什么区别?
- async返回Future.
- async*返回Stream.
1.async
这是异步调用。当一个函数被标记成async的时候,意味这个方法可能要从事耗时工作,比如说网络请求、处理图片等等。被async标记的方法会把返回值用Future包裹一下。
Future<int> doSomeLongTask() async {
await Future.delayed(const Duration(seconds: 1));
return 42;
}
我们可以通过await来获取Future里的返回值:
main() async {
int result = await doSomeLongTask();
print(result); // 等待一分钟后打印 '42'
}
2.async*
async*比async多了一个*,加上*其实是函数生成器的意思。被async*标记的函数会在返回一组返回值,这些返回值会被包裹在Stream中。async*其实是为yield关键字发出的值提供了一个语法糖。
Stream<int> countForOneMinute() async* {
for (int i = 1; i <= 60; i++) {
await Future.delayed(const Duration(seconds: 1));
yield i;
}
}
上面的其实就是异步生成器了。我们可以使用yield替代return返回数据,因为这个是时候我们的函数还在执行中。此时,我们就可以使用await for去等待Stream发出的每一个值了。
main() async {
await for (int i in countForOneMinute()) {
print(i); // 打印 1 到 60,一个秒一个整数
}
}
案例:我们需要每一秒钟请求一次接口,一共请求10次,来看看京东还剩多少茅台。
getMaoTai() async{
for (int i = 0; i <10; i++){
await Future.delayed(Duration(seconds: 1), ()async {
MaoTaiData data = await fetchMaoTaiData();
setState(){
//更新UI
};
});
}
上面的代码里使用了循环,然后每一秒钟请求依次接口,返回数据后调用setState()更新UI。这样做会导致你每隔一两秒就setState()一次,如果不怕性能问题,不怕产品经理打你,你这么玩玩。这个时候async*就应该上场了:
Stream<MaoTaiData> getData() async* {
for (int i = 0; i <10; i++) {
await Future.delayed(Duration(seconds: 1));
yield await fetchMaoTaiData();
}
}
这样我们就可以使用StreamBuilder包裹下Widget,就不必每次都去setState()了。
==================Dart中async和async*的区别end==============================
多接口请求返回后统一处理
1.Future.wait([response1, response2])结合Completer实现
Future<void> fetchData() async {
try {
var completer = Completer<List<dynamic>>();
var response1 = http.get(Uri.parse('https://example.com/data1'));
var response2 = http.get(Uri.parse('https://example.com/data2'));
Future.wait([response1, response2]).then((results) {
var data1 = jsonDecode(results[0].body);
var data2 = jsonDecode(results[1].body);
completer.complete([data1, data2]);
});
var results = await completer.future;
print(results[0]);
print(results[1]);
} catch (e) {
print('Error: $e');
}
}
2.Future.wait([response1, response2])结合FutureBuilder实现
Future fetchData() async {
var response1 = http.get(Uri.parse('https://example.com/data1'));
var response2 = http.get(Uri.parse('https://example.com/data2'));
return Future.wait([response1, response2]);
}
FutureBuilder(
future: fetchData(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// 说明有数据
...
}
})
3.自定义zipWith
扩展方法,返回多个值,使用的是dart3.0新特性Record
Flutter - Dart 3α 新特性 Record 和 Patterns 的提前预览讲解 · GitBook
Future<void> main() async {
//返回两个值
final (name, year) = await Future.value("andrew")
.zipWith(Future.value(1984));
print('name:$name ,year:$year');
//打印值:name:andrew ,year:1984
//返回三个值
final ((name, year), married) = await Future.value("andrew")
.zipWith(Future.value(1984))
.zipWith(Future.value(false));
print('name:$name ,year:$year , married:$married');
//打印值:name:andrew ,year:1984 , married:false
}
extension FutureZipX<T> on Future<T> {
Future<(T, T2)> zipWith<T2>(Future<T2> future2) async {
late T result1;
late T2 result2;
await Future.wait(
[then((it) => result1 = it), future2.then((it) => result2 = it)]);
return (result1, result2);
}
}