15*:Flutter之事件队列、微任务队列、多线程(timer、flutter、then、scheduleMicrotask、Isolate.spawn、compute)
问题
1:microtask queue
的优先级高于 event queue
,所以如果 microtask queue
有太多的微任务, 那么就可能会霸占住当前的event loop
。
2:then优先级高于Flutter
3: isolate高于微任务
目录
预备
我们所熟悉的前端开发框架大都是事件驱动的。事件驱动意味着你的程序中必然存在事件循环和事件队列。事件循环会不停的从事件队列中获取和处理各种事件。也就是说你的程序必然是支持异步的。
在Android中这样的结构是Looper/Handler;
在iOS中是RunLoop;
在JavaScript中是Event Loop。
同样的Flutter/Dart也是事件驱动的,也有自己的Event Loop。而且这个Event Loop和JavaScript的很像,很像。(毕竟Dart是想替换JS来着)。下面我们就来了解一下Dart中的Event Loop。
/* * 1,需要注意返回值,=>是return的简写,所以1、2、3的then是compute的后续,所以是在多线程中执行,无序的。 * 2,Future和自己的then是一体的,必定会同步执行,而scheduleMicrotask则是后续添加的所以最后执行 * * */ void combinText() { Future(() => compute(comFunc, '1')).then((val) => print(val)); //1 Future(() => compute(comFunc, '2')).then((val) => print(val)); Future(() => compute(comFunc, '3')).then((val) => print(val)); Future(() { compute(comFunc, '4'); return '4处理'; }).then((val) => print(val)); Future(() { compute(comFunc, '5'); return '5处理'; }).then((val) => print(val)); Future(() { compute(comFunc, '6'); return '6处理'; }).then((val) => print(val)); Future(() { compute(comFunc, '7'); scheduleMicrotask(() { print('8处理'); //2 }); return '7处理'; }).then((val) => print(val)); } /* * computeTest是Isolate的高层封装 * */ void computeTest() { //创建Port ReceivePort port = ReceivePort(); compute(comFunc, '初始值').then((val) => print(val)); } String comFunc(str) { return '${str}处理'; } /* * Isolate 多线程 * Isolate 看起来更加像进程.因为有独立的内存空间! * ReceivePort如果使用到变量,变量是进行深拷贝的值拷贝.内部修改值并不会影响外部变量本身,不用担心多线程的资源抢夺问题!不需要锁! * */ Future<void> IsolateTest() async { //创建Port ReceivePort port = ReceivePort(); //创建isolate Isolate iso = await Isolate.spawn(isoFunc, port.sendPort); port.listen((val) { print('内部a=$a'); a = val; port.close(); iso.kill(); }); sleep(Duration(seconds: 1)); print('外部a=$a'); } int a = 1; void isoFunc(SendPort port) { sleep(Duration(seconds: 1)); a = 200; print(port); port.send(100); } //开始 , 5, 3,6,8,7,1,4,10,2,9 void testFuture4() { Future x1 = Future(() => null); x1.then((value) { print('6'); scheduleMicrotask(() => print('7')); }).then((value) => print('8')); Future x = Future(() => print('1')); x.then((value) { print('4'); Future(() => print('9')); }).then((value) => print('10')); Future(() => print('2')); scheduleMicrotask(() => print('3')); print('5'); } /* * scheduleMicrotask微任务 * 在同一方法体中微任务优先级高于Future, * */ void MicrotTest() { print('进入'); Future(() { print('A'); scheduleMicrotask(() { print('A ---- scheduleMicroTask'); }); return Future(() => print('A--Future')); }).then((value) => print('A结束')); scheduleMicrotask(() { print('scheduleMicroTask'); }); } /* * Future异步组 * 在组中是按照同步执行 * */ void FutureGroup() { print('进入'); Future.wait([ Future.sync(() { sleep(Duration(seconds: 2)); print('结束1'); return '任务一'; }), Future.sync(() { sleep(Duration(seconds: 1)); print('结束2'); return '任务二'; }), ]).then((value) { print(value); }); print('结束'); } /* * Future也可以同步执行 * */ void FutureSync() { print('进入'); Future.sync(() { sleep(Duration(seconds: 1)); print('异步操作'); }); print('结束'); } //future的优先级比then低 /* * Future执行完之后, 会将涉及到的所有then进行一次性添加 * */ void FutureThenOrder() { Future(() { sleep(Duration(seconds: 1)); return '第一个异步处理'; }).then((e) { print(e); return Future(() { sleep(Duration(seconds: 1)); return '第二个异步处理'; }); }).then((e) { print(e); return '第一个异步处理2'; }); } /* * 1,FutureOr<T>表示可以返回一个Future对象或者是<T>实例 * 2,catchError在then前时,无法阻止then的执行,因为当前then实际上是在捕获catcherror这个Future * 3, catchError的闭包返回值是依附上一层的<T>,如果上一层没有返回值,catcherror中的返回值需要单声明变量后使用 * 4, 超时 * */ Future<void> throwError() async { print('进入'); Future future = Future(() { //1 print('异步操作'); sleep(Duration(seconds: 1)); // return '异步完成'; throw Exception('出错了'); }) .then((val) { print('第一次then:${val}'); return '第一次then结束'; }) .catchError((e) { print('errpr:${e}'); return '错误处理'; //3 }) .then((e) => print(e)) //2 .timeout(Duration(seconds: 1)); //4 print('结束'); } /* * 1,使用await以后下方所有代码都将进行同步执行 * 2,async/await必须成对出现,使用async进行方法的修饰后返回值必须也是Future<T> * */ Future<void> getParams1() async { print('进入'); await Future(() { sleep(Duration(seconds: 1)); print('异步操作'); }); print('结束'); }
正文
1:Dart的Event Loop
Dart的事件循环如下图所示。和JavaScript的基本一样。循环中有两个队列。一个是微任务队列(MicroTask queue),一个是事件队列(Event queue)。
在Dart中,实际上有两种队列:
-
事件队列(event queue),包含所有的外来事件:I/O、mouse events、drawing events、timers、isolate之间的信息传递。
-
微任务队列(microtask queue),表示一个短时间内就会完成的异步任务。它的优先级最高,高于event queue,只要队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由 Dart内部产生。
在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。
Dart的事件循环的运行遵循以下规则:
- 首先处理所有微任务队列里的微任务。
- 处理完所有微任务以后。从事件队列里取1个事件进行处理。
- 回到微任务队列继续循环。
注意第一步里的所有,也就是说在处理事件队列之前,Dart要先把所有的微任务处理完。如果某一时刻微任务队列里有8个微任务,事件队列有2个事件,Dart也会先把这8个微任务全部处理完再从事件队列中取出1个事件处理,之后又会回到微任务队列去看有没有未执行的微任务。
总而言之,就是对微任务队列是一次性全部处理,对于事件队列是一次只处理一个。
这个流程要清楚,清楚了才能理解Dart代码的执行顺序。
正常情况下,一个 Future 异步任务的执行是相对简单的:
- 声明一个 Future 时,Dart 会将异步任务的函数执行体放入event queue,然后立即返回,后续的代码继续同步执行。
- 当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作。
2:Future用法
0:timer
Timer.run((){ print('a event'); });
1:Future+async
String _name = "dingding"; void main() { print("开始_name = ${_name}"); getData_async_future(); print("结束_name = ${_name}"); } //async 标记是一个异步函数 void getData_async_future() async { Future future = Future(() { //Futuren内部放耗时操作 for (int i = 0; i < 10000; i++) { _name = "lala"; } print("耗时操作"); return _name; print("耗时结束_name = ${_name}"); }); //使用then接收Future 中的回调 future.then((value) { print(value); print("耗时结束_name = ${_name}"); }); }
打印
flutter: 开始_name = dingding flutter: 结束_name = dingding flutter: 耗时操作 flutter: lala flutter: 耗时结束_name = lala
可以看到结束操作早于耗时操作,说明异步处理了耗时操作。
2:async 搭配await 实现同步执行
await:后面跟着一个Future,表示等待该异步任务完成,异步任务完成后才会继续往下执行。await只能出现在异步函数内部。能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式。
注意点
- 后面的操作必须是异步才能用awati
- 当前函数必须是异步函数
void getData_async_awati() async { print("start"); Future future = await Future(() { for (int i = 0; i < 10000; i++) { } print('耗时操作'); }); print("end"); } // 打印 // flutter: start // flutter: 耗时操作 // flutter: end
3:then、error、whenComplete 链式语法
- then:在Future内部函数处理完后就会有回调
- error:在Future内部抛出错误时,会有回调
- whenComplete:不管Future内部是否抛出错误,都会有回调 eg: 写法一:直接在then函数中取onError函数回调
void getData_async_future_error() async { //链式语法推荐 Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("网络异常"); }).then((value) { print('then 来了'); print(value); },onError: (error) { print('捕获了错误 error:${error.toString()}'); }).whenComplete(() => print('完成了')); //非链式语法 不推荐 Future future = Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("网络异常"); }); future.then((value) { print('then 来了'); print(value); },onError: (error) { print('捕获了错误 error:${error.toString()}'); }); future.whenComplete(() => print('完成了')); }
打印:
flutter: 捕获了错误 error:Exception: 网络异常
flutter: 完成了
面的then、whenComplete都是用链式语法,推荐使用这中方式
方式二:使用catchError
Future(() { for (int i = 0; i < 10000; i++) { } throw Exception("网络异常"); }).then((value) { print('then 来了'); print(value); }).catchError((error) {//处理错误 print('捕获了错误 error:${error.toString()}'); }).whenComplete(() => print('完成了'));
4:future执行顺序
//执行顺序按照编写顺序,类似队列
//then比Future优先级高
void futureOrder() { //执行顺序按照编写顺序,类似队列 //then比Future优先级高 Future(() { sleep(Duration(seconds: 1)); return '任务一'; }).then((value) { print('$value 结束'); return "任务五"; }).then((value) => print(value)); Future(() { return '任务二'; }).then((value) => print('$value 结束')); Future(() { return '任务三'; }).then((value) => print('$value 结束')); Future(() { return '任务四'; }).then((value) => print('$value 结束')); print('添加完毕'); }
打印
flutter: 开始_name = dingding flutter: 添加完毕 flutter: 结束_name = dingding flutter: 任务一 结束 flutter: 任务五 flutter: 任务二 结束 flutter: 任务三 结束 flutter: 任务四 结束
5:依赖
void futureDepandense() { Future.wait([ Future(() { print('任务一'); return "任务一"; }), Future(() { sleep(Duration(seconds: 1)); print('任务二'); return "任务二"; }), ]).then((value) {//注意此时value 就是一个数组 print('then:来了:${value[0]} + ${value[1]}'); print('任务3'); }); }
打印
flutter: 任务一 flutter: 任务二 flutter: then:来了:任务一 + 任务一 flutter: 任务3
6:scheduleMicrotask 微任务
- scheduleMicrotask 比Future异步任务高
- Future 在事件队列里面
- 在同级中,then 优先级 < scheduleMicrotask
- 在then函数中,then 优先级 > scheduleMicrotask
void dartLoop() { print('out task 1'); Future(() => print("A")).then((value) => print("A 结束")); Future(() => print("B")).then((value) => print("B 结束")); scheduleMicrotask(() { print('Microtask 1'); }); print('out task 2'); }
打印:
flutter: out task 1 flutter: out task 2 flutter: Microtask 1 flutter: A flutter: A 结束 flutter: B flutter: B 结束
7:多线程:Isolate
Dart中一般使用Isolate
实现多线程。Isolate
有独立的内存空间,不用考虑多线程时资源抢夺问题,即不需要锁。所以Isolate更像多进程操作。但是进程之间的通信也就相对麻烦,其使用port通信。
int a = 10; Future<void> dartIsolate() async { print('开始'); print("外部 a = ${a}"); Isolate.spawn(funcA,100); //创建Port ReceivePort port = ReceivePort(); //创建Isolate Isolate ISO = await Isolate.spawn(funcB,port.sendPort); port.listen((message) { print("portMessage ${message}"); a = message; //关闭端口 port.close(); //销毁ISO ISO.kill(); }); print('结束'); print("外部 a = ${a}"); } void funcA(int p) { a = p; print('funcA'); print("内部 a = ${a}");//a = 100 说明两个a不在同一片内存空间,间接说明 Isolate其实是开辟一个新的进程 } void funcB(SendPort sendPort) { print('funcB'); sendPort.send(1000); }
打印:
flutter: 开始_name = dingding flutter: 开始 flutter: 外部 a = 10 flutter: 结束_name = dingding flutter: funcA flutter: 内部 a = 100 flutter: funcB flutter: 结束 flutter: 外部 a = 10 flutter: portMessage 1000
8:更轻的compute
compute 是对Isolate的封装,更轻量的。线程通信更加方便
//compute 是对Isolate的封装,更轻量的。线程通信更加方便 void computeTest() async { print("start"); //compute能直接拿到回调,不需要做多余的进程端口通信 int x = await compute(funcC,10); print('x = ${x}'); print("end"); } int funcC(int p) { print('funcC'); return p + 100; }
打印:
flutter: start flutter: funcC flutter: x = 110 flutter: end
面试题
下面打印顺序是什么
void dartLoopTest() { Future x0 = Future(() => null); Future x = Future(() => print('1')); Future(() => print('2')); scheduleMicrotask(() => print('3')); x.then((value) { print('4'); Future(() => print('5')); }).then((value) => print('6')); print('7'); x0.then((value) { print('8'); scheduleMicrotask(() { print('9'); }); }).then((value) => print('10')); }
打印:
flutter: 7 flutter: 3 flutter: 8 flutter: 10 flutter: 9 flutter: 1 flutter: 4 flutter: 6 flutter: 2 flutter: 5
注意
引用
1:Dart中的异步编程——Future、async和await