不管是在Android/IOS,还是Web、后台、PHP,GO等等编程语言中,都会有同步异步的概念。简单说,同步就是按照代码顺序执行,由上至下。
但是,同步也会导致出现问题阻塞,比如同步时,我们中间需要处理耗时操作,一旦是同步的,就会导致后续事件无法处理,直观的感受案例:我们在手机
上点击屏幕翻页,但是视频可能半分钟之后才响应,也就是阻塞无响应。
在java/swift/kotlin中,如果异步,是需要开子线程去执行(注意kotlin有协程,可以不需要)异步操作,十个耗时任务,可以开启十个子线程同时执行,但同时也带来了大量资源的耗费以及内存共享问题需要加锁解决,复杂情况可能会导致死锁。基于此观念,java后台开发出现了很多著名的框架,springBoot/SpringCloud/k8s等等。
在Dart语言中,只有单线程,那么它是如何处理同步异步问题了。
事件异步模型,简单说:单线程中存在一个事件循环和一个事件队列,比如Android中Handler和MessageQueue就是如此设计的,但也有所不同。事件循环,不断地从事件队列中取数据,遇到耗时的事件时,不会停下来等待结果,而是跳过耗时事件,继续执行下一个事件。等所有耗时事件完成之后,在进行查看耗时的结果。因此,耗时事件不会阻塞整个事件循环,这让它后面的事件也会有机会得到执行
三者对比比较:
| 模型 | CPU资源 | 内存占用 | 计算密集型 | 复杂度 | I/0流 |
| ------ | ------- | -------- | ---------- | | |
| 单线程 | 1核 | 低 | 低 | 低 | 卡死 |
| 多线程 | 多核 | 高 |低| 高 | 高 |
| 基于事件 | 1核 | 低 |低| 低 | 高 |
通过对比,可以了解到,基于事件模型,不擅长计算密集型的操作,这种异步模型往往用于网络服务器并发。
Dart 是事件驱动的体系结构,该结构基于具有单个事件循环和两个队列的单线程执行模型。 Dart虽然提供调用堆栈。 但是它使用事件在生产者和消费者之间传输上下文。 事件循环由单个线程支持,因此根本不需要同步和锁定。
Dart 的两个队列分别是:
MicroTask queue
微任务队列Event queue
事件队列Dart事件循环执行如上图所示
MicroTask
队列是否为空,不是则先执行MicroTask
队列总结:MicroTask中任务,需要尽快执行处理,否则Event队列无法执行,Event队列大部分是鼠标按键操作或者I/O流操作,直观感受就是被卡住。
import 'dart:async';
void main() {
//1.使用scheduleMicrotask
scheduleMicrotask(myTask);
//2.使用Future对象添加
new Future.microtask(myTask);
}
void myTask(){
print("this is my task");
}
import 'dart:async';
void main() {
new Future(myTask);
}
void myTask(){
print("this is my task");
}
注意:延迟队列,是加入到Event队列中
import 'dart:async';
void main() {
new Future.delayed(new Duration(seconds:1),(){
print('task delayed');
});
}
import 'dart:async';
void main() {
print('开始');
new Future(() {
print('这是event队列');
});
new Future.microtask(() => {
print('这是micro队列')
});
new Future.delayed(new Duration(seconds:1),(){
print('task delayed');
});
print('结束');
}
result:
E:\flutter\bin\cache\dart-sdk\bin\dart.exe --enable-asserts F:\DartDemo2\.idea\day01\DartAsync\03.dart
开始
结束
这是micro队列
这是event队列
task delayed
Process finished with exit code 0
说明:Future类是对未来结果的一个代理,并不是返回任务值,任务值需要获取。
Future()
//默认Event队列Future.microtask()
//mocro队列Future.sync()
//立即执行Future.value()
Future.delayed()
Future.error()
其中sync
是同步方法,任务会被立即执行
任务完成后,需要一个回调,拿到任务返回结果,回调会被立即执行,不会再加入队列中。
import 'dart:async';
int myTask() {
return 2;
}
void main() {
new Future(() {
return myTask();
}).then((res) { //链式调用
print("async task complete $res");
return res;
}).then((res) {//链式调用
print("async task after$res");
});
}
简化 Future调用:
遇到await之后,会将任务发到队列中
import 'dart:io';
Future doTask() async {
sleep(Duration(seconds: 2));
return "ok";
}
// 定义一个函数用于包装
void test() async {
var r = await doTask();
print(r);
}
void main() {
test();
}
模拟链式
void getData() async {
var res1 = await getNetworkData("agrument1");
var res2 = await getNetworkData(res1);
var res3 = await getNetworkData(res2);
print(res3);
}
Future getNetworkData(String arg) {
return Future((){
sleep(Duration(seconds: 3));
return "Hello word" +arg;
});
}
非常耗时的任务添加到事件队列后,仍然会拖慢整个事件循环的处理,甚至是阻塞。可见基于事件循环的异步模型仍然是有很大缺点的,这时候我们就需要Isolate
。
简单来说,可以将其理解为线程,但是不同于线程,最佳解释为微线程或者协程概念。二者区别在于:线程资源可以共享,有加锁的概念,而协程是没有的,每个协程Isolate都是独立的,每个Isolate
都有自己的事件循环,它们之间只能通过发送消息通信,所以它的资源开销低于线程。
这个dart用法中挺麻烦的,暂时过滤,再Flutter中使用比较容易,因为Flutter给我们封装好了。
常见使用场景:
1.超大文件的下载
2.超大文件的拷贝
3.加密
4.图像处理,比如裁剪
Stream
和 Future
都是Dart中异步编程的核心内容
Stream
是Dart语言中的所谓异步数据序列的东西,简单理解,其实就是一个异步数据队列而已。我们知道队列的特点是先进先出的,Stream
也正是如此
更形象的比喻,Stream
就像一个传送带。可以将一侧的物品自动运送到另一侧。如上图,在另一侧,如果没有人去抓取,物品就会掉落消失。
但如果我们在末尾设置一个监听,当物品到达末端时,就可以触发相应的响应行为。
//该方法从整数0开始,在指定的间隔时间内生成一个自然数列,以上设置为每一秒生成一次,callback函数用于对生成的整数进行处理,处理后再放入Stream中。这里并未处理,直接返回了。要注意,这个流是无限的,它没有任何一个约束条件使之停止。在后面会介绍如何给流设置条件。
void main(){
test();
}
test() async{
// 使用 periodic 创建流,第一个参数为间隔时间,第二个参数为回调函数
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
// await for循环从流中读取
await for(var i in stream){
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value){
return value * 2;
}
————————可以用于做定时器
Future
创建Stream
该方法从一个Future
创建Stream
,当Future
执行完成时,就会放入Stream
中,而后从Stream
中将任务完成的结果取出。这种用法,很像异步任务队列。
//该方法从一个Future创建Stream,当Future执行完成时,就会放入Stream中,而后从Stream中将任务完成的结果取出。这种用法,很像异步任务队列。
void main(){
test();
}
test() async{
print("test start");
Future fut = Future((){
return "async task";
});
// 从Future创建Stream
Stream stream = Stream.fromFuture(fut);
await for(var s in stream){
print(s);
}
print("test end");
}
import 'dart:io';
//从多个Future创建Stream,即将一系列的异步任务放入Stream中,每个Future按顺序执行,执行完成后放入Stream
void main() {
test();
}
test() async{
print("test start");
Future fut1 = Future((){
// 模拟耗时5秒
sleep(Duration(seconds:2));
return "async task1";
});
Future fut2 = Future((){
sleep(Duration(seconds: 2));
return "async task2";
});
// 将多个Future放入一个列表中,将该列表传入
Stream stream = Stream.fromFutures([fut1,fut2]);
await for(var s in stream){
print(s);
}
print("test end");
}
该方法从一个集合创建Stream
,用法与上面例子大致相同
//该方法从一个集合创建Stream,用法与上面例子大致相同
void main(){
test();
}
test() async{
// 使用 periodic 创建流,第一个参数为间隔时间,第二个参数为回调函数
// 从一个列表创建`Stream`
Stream stream = Stream.fromIterable([1,2,3]);
// await for循环从流中读取
await for(var i in stream){
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value){
return value * 2;
}
这是Dart2.5 新增的方法,用于从单个值创建Stream
//这是Dart2.5 新增的方法,用于从单个值创建Stream
void main(){
test();
}
test() async{
Stream stream = Stream.value(false);
// await for循环从流中读取
await for(var i in stream){
print(i);
}
}
监听Stream
,并从中获取数据也有三种方式
其中listen:
onError
:发生Error时触发onDone
:完成时触发unsubscribeOnError
:遇到第一个Error时是否取消监听,默认为false
//该方法从整数0开始,在指定的间隔时间内生成一个自然数列,以上设置为每一秒生成一次,callback函数用于对生成的整数进行处理,处理后再放入Stream中。这里并未处理,直接返回了。要注意,这个流是无限的,它没有任何一个约束条件使之停止。在后面会介绍如何给流设置条件。
import 'dart:async';
void main() {
test();
}
test() async {
// 使用 periodic 创建流,第一个参数为间隔时间,第二个参数为回调函数
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
// await for循环从流中读取
// await for (var i in stream) {
// print(i);
// }
// print("object");
// // 使用forEach,传入一个函数进去获取并处理数据
// stream.forEach((int x) {
// print(x);
// });
// stream.listen循环从流中读取
// stream.listen((x) {
// print(x);
// }, onError: (e) => print(e),
// onDone: () => print("onDone")
// );
stream.listen(print);//简写,如同kotlin
}
// 可以在回调函数中对值进行处理修改值
int callback(int value) {
return value * 2;
}
take:用于限制Stream
中的元素数量
takewhile:除了限制Stream个数,还判断bool
// Stream take(int count) 用于限制Stream中的元素数量
void main(){
test();
}
test() async{
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
// 当放入三个元素后,监听会停止,Stream会关闭
stream = stream.take(3);
await for(var i in stream){
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value){
return value * 2;
}
// Stream take(int count) 用于限制Stream中的元素数量
void main(){
test();
}
test() async{
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
// 当放入三个元素后,监听会停止,Stream会关闭
stream = stream.takeWhile((x){
// 对当前元素进行判断,不满足条件则取消监听
return x <= 3;
});
await for(var i in stream){
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value){
return value * 2;
}
注意:该方法只是从Stream
中获取元素时跳过,被跳过的元素依然是被执行了的,所耗费的时间依然存在,其实只是跳过了执行完的结果而已。
// 表示从Stream中跳过两个元素
void main(){
test();
}
test() async{
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
stream = stream.take(5);
// 表示从Stream中跳过两个元素
stream = stream.skip(2);
await for(var i in stream){
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value){
return value * 2;
}
将Stream
中所有数据存储在List中
// Future> toList() 表示将Stream中所有数据存储在List中
void main() {
test();
}
test() async {
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
stream = stream.take(5);
List data = await stream.toList();
for (var i in data) {
print(i);
}
}
// 可以在回调函数中对值进行处理修改值
int callback(int value) {
return value * 2;
}
//等待并获取流中所有数据的数量
void main() {
test();
}
test() async{
Stream stream = Stream.periodic(Duration(seconds: 1), callback);
stream = stream.take(5);
var len = await stream.length;
print(len);
}
// 可以在回调函数中对值进行处理修改值
int callback(int value) {
return value * 2;
}
onListen
注册监听时回调onPause
当流暂停时回调onResume
当流恢复时回调onCancel
当监听器被取消时回调sync
当值为true
时表示同步控制器SynchronousStreamController
,默认值为false
,表示异步控制器import 'dart:async';
//它实际上就是Stream的一个帮助类,可用于整个 Stream 过程的控制。
void main() {
test();
}
test() async{
// 创建
StreamController streamController = StreamController();
// 放入事件
streamController.add('element_1');
streamController.addError("this is error");
streamController.sink.add('element_2');
streamController.stream.listen(
print,
onError: print,
onDone: ()=>print("onDone"));
}
import 'dart:async';
//还可以在StreamController中传入一个指定的stream
void main() {
test();
}
test() async{
Stream stream = Stream.periodic(Duration(seconds: 1), (e)=>e);
stream = stream.take(5);
StreamController sc = StreamController();
// 将 Stream 传入
sc.addStream(stream);
// 监听
sc.stream.listen(
print,
onDone: ()=>print("onDone"));
}
/**
* onListen 注册监听时回调
onPause 当流暂停时回调
onResume 当流恢复时回调
onCancel 当监听器被取消时回调
sync 当值为true时表示同步控制器SynchronousStreamController,默认值为false,表示异步控制器
*/
test2() async{
// 创建
StreamController sc = StreamController(
onListen: ()=>print("onListen"),
onPause: ()=>print("onPause"),
onResume: ()=>print("onResume"),
onCancel: ()=>print("onCancel"),
sync:false
);
StreamSubscription ss = sc.stream.listen(print);
sc.add('element_1');
// 暂停
ss.pause();
// 恢复
ss.resume();
// 取消
ss.cancel();
// 关闭流
sc.close();
}
import 'dart:async';
//还可以在StreamController中传入一个指定的stream
void main() {
test2();
}
/**
* onListen 注册监听时回调
onPause 当流暂停时回调
onResume 当流恢复时回调
onCancel 当监听器被取消时回调
sync 当值为true时表示同步控制器SynchronousStreamController,默认值为false,表示异步控制器
*/
test2() async{
// 创建
StreamController sc = StreamController(
onListen: ()=>print("onListen"),
onPause: ()=>print("onPause"),
onResume: ()=>print("onResume"),
onCancel: ()=>print("onCancel"),
sync:false
);
StreamSubscription ss = sc.stream.listen(print);
sc.add('element_1');
// 暂停
ss.pause();
// 恢复
ss.resume();
// 取消
ss.cancel();
// 关闭流
sc.close();
}
import 'dart:async';
/**
* 前面已经说了单订阅流的特点,而广播流则可以允许多个监听器存在,就如同广播一样,凡是监听了广播流,每个监听器都能获取到数据。
* 要注意,如果在触发事件时将监听者正添加到广播流,则该监听器将不会接收当前正在触发的事件。如果取消监听,监听者会立即停止接收事件。
* 有两种方式创建广播流,一种直接从Stream创建,另一种使用StreamController创建
*
*
*
* asBroadcastStream
*/
void main() {
test();
}
test() async{
// 调用 Stream 的 asBroadcastStream 方法创建
Stream stream = Stream.periodic(Duration(seconds: 1), (e)=>e)
.asBroadcastStream();
stream = stream.take(5);
stream.listen(print);
stream.listen(print);
}
添加事件
import 'dart:async';
/**
*
*
*
* broadcast
*/
void main() {
test();
}
test() async{
// 创建广播流
StreamController sc = StreamController.broadcast();
sc.stream.listen(print);
sc.stream.listen(print);
sc.add("event1");
sc.add("event2");
}
该类可以使我们在Stream
上执行数据转换。然后,这些转换被推回到流中,以便该流注册的所有监听器可以接收
factory StreamTransformer.fromHandlers({
void handleData(S data, EventSink sink),
void handleError(Object error, StackTrace stackTrace, EventSink sink),
void handleDone(EventSink sink)
})
handleData
:响应从流中发出的任何数据事件。提供的参数是来自发出事件的数据,以及EventSink
,表示正在进行此转换的当前流的实例handleError
:响应从流中发出的任何错误事件handleDone
:当流不再有数据要处理时调用。通常在流的close()
方法被调用时回调
import 'dart:async';
//StreamController类似于RxJava中flatMap以及map函数
void main() {
test();
}
void test() {
StreamController sc = StreamController();
// 创建 StreamTransformer对象
StreamTransformer stf = StreamTransformer.fromHandlers(
handleData: (int data, EventSink sink) {
// 操作数据后,转换为 double 类型
sink.add((data * 2).toDouble());
},
handleError: (error, stacktrace, sink) {
sink.addError('wrong: $error');
},
handleDone: (sink) {
sink.close();
},
);
// 调用流的transform方法,传入转换对象
Stream stream = sc.stream.transform(stf);
stream.listen(print);
// 添加数据,这里的类型是int
sc.add(1);
sc.add(2);
sc.add(3);
// 调用后,触发handleDone回调
// sc.close();
}