事件循环、Isolate
开始前我们需要明白 Dart 是单线程的并且 Flutter 依赖于 Dart
如果你知道js 中的event loop 将很好理解dart的整个异步过程
先看一段代码
import 'dart:async';
Future eventLoop() async{
print('A');
Future((){
print('F');
scheduleMicrotask((){print('H');});
Future((){
print('M');
}).then((_){
print('N');
});
}).then((_){
print('G');
});
Future((){
print('I');
}).then((_){
print('J');
});
scheduleMicrotask(text1);
scheduleMicrotask((){print('D');});
print('B');
}
void text1() {
print('C');
scheduleMicrotask((){print('E');});
Future((){
print('K');
}).then((_){
print('L');
});
}
你只到输出结果吗
正确的输出顺序是: A B C D E F G H I J K L M N
eventLoop
1、MicroTask 队列
微任务队列,一般使用scheduleMicroTask方法向队列中添加
这是大多数时候你不必使用的东西。比如,在整个 Flutter 源代码中 scheduleMicroTask() 方法仅被引用了 7 次, 所以最好优先考虑使用 Event 队列
2、Event 队列
I/O、手势、绘图、计时器、流、futures等等异步操作都将进入event队列
尽量使用事件队列可以使微任务队列更短,降低事件队列卡死的可能性
代码执行顺序
首先我们知道dart是单线程的,所以dart的代码执行顺序是: 同步代码依次执行 碰到异步代码先进对应的队列中,然后继续执行下面的代码 当同步代码执行完毕,先去看MicroTask 队列中的任务,将MicroTask队列中的任务依次执行完毕 MicroTask中的任务执行完毕后,再去看Event 队列中的任务,event队列出一个任务 然后执行 , 然后回到第三步 循环 直到所有队列都清空
Isolate
Isolate 是 Dart 中的 线程, Flutter的代码都是默认跑在root isolate上的
「Isolate」在 Flutter 中并不共享内存。不同「Isolate」之间通过「消息」进行通信。
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
//一个普普通通的Flutter应用的入口
//main函数这里有async关键字,是因为创建的isolate是异步的
void main() async{
runApp(MyApp());
//asyncFibonacci函数里会创建一个isolate,并返回运行结果
print(await asyncFibonacci(20));
}
//这里以计算斐波那契数列为例,返回的值是Future,因为是异步的
Future asyncFibonacci(int n) async{
//首先创建一个ReceivePort,为什么要创建这个?
//因为创建isolate所需的参数,必须要有SendPort,SendPort需要ReceivePort来创建
final response = new ReceivePort();
//开始创建isolate,Isolate.spawn函数是isolate.dart里的代码,_isolate是我们自己实现的函数
//_isolate是创建isolate必须要的参数。
await Isolate.spawn(_isolate,response.sendPort);
//获取sendPort来发送数据
final sendPort = await response.first as SendPort;
//接收消息的ReceivePort
final answer = new ReceivePort();
//发送数据
sendPort.send([n,answer.sendPort]);
//获得数据并返回
return answer.first;
}
//创建isolate必须要的参数
void _isolate(SendPort initialReplyTo){
final port = new ReceivePort();
//绑定
initialReplyTo.send(port.sendPort);
//监听
port.listen((message){
//获取数据并解析
final data = message[0] as int;
final send = message[1] as SendPort;
//返回结果
send.send(syncFibonacci(data));
});
}
int syncFibonacci(int n){
return n <2 ? n : syncFibonacci(n-2) &#43; syncFibonacci(n-1);
}
因为Root isolate会负责渲染&#xff0c;还有UI交互&#xff0c;如果我们有一个很耗时的操作呢&#xff1f;前面知道isolate里是一个event loop(事件循环)&#xff0c;如果一个很耗时的task一直在运行&#xff0c;那么后面的UI操作都被阻塞了&#xff0c;所以如果我们有耗时的操作&#xff0c;就应该放在isolate里&#xff01;
Stream(流)
什么是流&#xff1f;
这个大机器就是StreamController&#xff0c;它是创建流的方式之一。 StreamController有一个入口&#xff0c;叫做sink sink可以使用add方法放东西进来&#xff0c;放进去以后就不再关心了。 当有东西从sink进来以后&#xff0c;我们的机器就开始工作 StreamController有一个出口&#xff0c;叫做stream 机器处理完毕后就会把产品从出口丢出来&#xff0c;但是我们并不知道什么时候会出来&#xff0c;所以我们需要使用listen方法一直监听这个出口 而且当多个物品被放进来了之后&#xff0c;它不会打乱顺序&#xff0c;而是先入先出
使用Stream
StreamController controller &#61; StreamController();
//监听这个流的出口&#xff0c;当有data流出时&#xff0c;打印这个data
StreamSubscription subscription &#61;
controller.stream.listen((data)&#61;>print("$data"));
controller.sink.add(123);
// 输出&#xff1a; 123
你需要将一个方法交给stream的listen函数&#xff0c;这个方法入参(data)是我们的StreamController处理完毕后产生的结果&#xff0c;我们监听出口&#xff0c;并获得了这个结果(data)。这里可以使用lambda表达式&#xff0c;也可以是其他任何函数
transform
如果你需要更多的控制转换&#xff0c;那么请使用transform()方法。他需要配合StreamTransformer进行使用。
StreamController controller &#61; StreamController();
final transformer &#61; StreamTransformer.fromHandlers(
handleData:(value, sink){
if(value&#61;&#61;100){
sink.add("你猜对了");
}
else{ sink.addError(&#39;还没猜中&#xff0c;再试一次吧&#39;);
}
});
controller.stream
.transform(transformer)
.listen(
(data) &#61;> print(data),
onError:(err) &#61;> print(err));
controller.sink.add(23);
//controller.sink.add(100);
// 输出&#xff1a; 还没猜中&#xff0c;再试一次吧
StreamTransformer是我们stream的检查员&#xff0c;他负责接收stream通过的信息&#xff0c;然后进行处理返回一条新的流。 S代表之前的流的输入类型&#xff0c;我们这里是输入一个数字&#xff0c;所以是int。 T代表转化后流的输入类型&#xff0c;我们这里add进去的是一串字符串&#xff0c;所以是String。 handleData接收一个value并创建一条新的流并暴露sink&#xff0c;我们可以在这里对流进行转化
Stream的种类 "Single-subscription" streams 单订阅流 "broadcast" streams 多订阅流
单订阅流
StreamController controller &#61; StreamController();
controller.stream.listen((data)&#61;> print(data));
controller.stream.listen((data)&#61;> print(data));
controller.sink.add(123);
// 输出: Bad state: Stream has already been listened to. 单订阅流不能有多个收听者。
单个订阅流在流的整个生命周期内仅允许有一个listener
多订阅流
StreamController controller &#61; StreamController();
//将单订阅流转化为广播流
Stream stream &#61; controller.stream.asBroadcastStream();
stream.listen((data)&#61;> print(data));
stream.listen((data)&#61;> print(data));
controller.sink.add(123);
// 输出&#xff1a; 123 123
广播流允许任意数量的收听者&#xff0c;且无论是否有收听者&#xff0c;他都能产生事件。所以中途进来的收听者将不会收到之前的消息。
参考 深入了解isolate