文章目录
Dart(三)泛型、异步知多少
泛型
eg. List<E>
、Map<E>
、Set<E>
通常情况下,会使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。
为什么使用泛型?
- 提高代码质量、提高代码可读性
- 减少重复代码
- 保证类型安全
比如下面两段代码是否可以编译通过?
class Demo1 {
void func1() {
List<String> stringList = [];
stringList.add(1);
}
}
class Demo1 {
void func2() {
List stringList = [];
stringList.add(1);
stringList.add('value');
}
}
//弊端?
泛型的使用
在集合中使用泛型
-
上述方式,直接通过指定类型声明集合类型
-
List , Set 和 Map 字面量也是可以参数化的。 参数化字面量和之前的字面量定义类似, 对于 List 或 Set 只需要在声明语句前加
<*type*>
前缀, 对于 Map 只需要在声明语句前加<*keyType*, *valueType*>
前缀var List = <String>['tt']; var Set = <String>{'tt'}; var Map = <String, String> { 'key': 'tt', };
-
通过集合的构造函数指定类型
以
List
为例var list3 = List<String>.of(['TT', 'OO']);
在类中使用泛型
class Demo3<T> {
late T variate;
T print(T param) {
// ignore: unused_local_variable
T innerParam;
var param1;
return param1 as T;
}
}
泛型方法
class Demo4 {
List<E> getData<E>(E element) {
return [];
}
R getElement<R>() {
return null as R;
}
}
在类中,哪里可以使用范型
- 类
- 成员变量
- 函数的参数列表、返回值
- 局部变量
- 使用
extends
关键字,限制泛型范围
class Demo4<T extends TypeClass> {
}
使用:
class TypeClass {}
class TypeClassChild extends TypeClass {}
class TypeClass1 {}
main() {
var demo1 = Demo3<TypeClass>();
Demo3<TypeClass1> demo2 = Demo3();
var demo3 = Demo4<TypeClass>();
// var demo4 = Demo4<TypeClass1>(); //compile error;
var demo5 = Demo4<TypeClassChild>();
}
思考:运行时,如何判断集合的类型?
Dart
中泛型类型是 固化的,也就是说它们在运行时是携带着类型信息的。
Java
中的泛型会被 擦除 ,也就是说在运行时泛型类型参数的信息是不存在的。 在Java中,可以测试对象是否为 List 类型, 但无法测试它是否为List<String>
。
异步
概念
- 同步
简单说,就是方法调用时按照代码编写的顺序,从上到下依次调用执行,比较常见的调用方式。
- 异步
为了解决同步调用耗时操作阻塞的问题,两条腿或多条腿走路。
-
异步实现方式
- 多线程
- 可以并发执行,解决耗时问题
- 耗费系统资源
- 并发锁问题,产生死锁
- 异步模型
简单说就是在某个单线程中存在一个事件循环和一个事件队列。
事件循环不断的从事件队列中取出事件来执行,这里的事件就好比是一段代码,每当遇到耗时的事件时,事件循环不会停下来等待结果,它会跳过耗时事件,继续执行其后的事件。当不耗时的事件都完成了,再来查看耗时事件的结果。因此,耗时事件不会阻塞整个事件循环,这让它后面的事件也会有机会得到执行。
- 多线程
Dart中的异步
、Event Loop
处理机制
Dart是单线程模型,是事件驱动体系结构,该结构基于单个事件循环和两个事件队列的单线程执行模型。
Dart中的两个事件队列
- 微任务队列(
MicroTask Queue
)- 通常有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。
- 事件队列(
Event Queue
)- 包含所有外来的事件:I/O,mouse events,drawing events,timers,isolate之间的message等。
event队列包含Dart和来自系统其它位置的事件。
但microtask队列只包含来自当前isolate的内部代码。
Dart中的事件循环机制
- 先查看
MicroTask
队列是否为空,不是则先执行MicroTask
队列 - 一个
MicroTask
执行完后,检查有没有下一个MicroTask
,直到MicroTask
队列为空,才去执行Event
队列 - 在
Event
队列取出一个事件处理完后,再次返回第一步,去检查MicroTask
队列是否为空
MicroTask
队列优先级高于Event
队列
怎样向MicroTask
队列和Event
队列添加事件
import 'dart:async';
-
向
MicroTask队列
添加事件-
使用**
scheduleMicrotask
**方法添加scheduleMicrotask(() { print('this is microtask2'); });
-
使用**
Future
**对象添加Future.microtask(() { print('this is microtask1'); }); /// /// microtask的实现 factory Future.microtask(FutureOr<T> computation()) { _Future<T> result = new _Future<T>(); scheduleMicrotask(() { try { result._complete(computation()); } catch (e, s) { _completeWithErrorCallback(result, e, s); } }); return result; }
-
-
向
Event队列
添加事件-
使用**
Future
**对象添加Future(() { print('this is my task1'); });
-
我们可以认为***Dart 为 Event Queue 的任务建立提供了一层封装,叫作 Future。***通过Future
可以对事件结果进行操作。
异步之Future
什么是
Future
,顾名思义,表示一件将来会发生的事情(也就是不会立即执行),将来
可以从Future
中取到一个值
,当一个方法返回一个Future
时,发生两件事:
- 这个方法将某件事情排队,返回一个未完成的
Future
。- 这个方法事情完毕后,
Future
的状态会变成已经完成,这个时候可以取到这件事情的返回值。
Future
表示一个异步操作的最终完成(或失败)及其结果值的表示,简单来说,它就是用来处理异步操作的,异步处理成功就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future
只会对应一个结果,要么成功,要么失败。
创建Future
值得注意的一点
延时任务Future.delayed
中的duration
延时事件真的靠谱吗?
分析如下代码输出结果:(future.delay.dart)
import 'dart:async';
import 'dart:io';
void main() {
print("main start");
Future.delayed(Duration(seconds: 1), () {
print('task delayed');
});
Future(() {
// 模拟耗时5秒
sleep(Duration(seconds: 5));
print("5s task");
});
print("main stop");
}
///
/// 输出结果
main start
main stop
5s task
task delayed
Exited
结论:
-
Future.delayed
是何时把事件加入到Event队列的?-
延时时间到了之后
factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) { if (computation == null && !typeAcceptsNull<T>()) { throw ArgumentError.value( null, "computation", "The type parameter is not nullable"); } _Future<T> result = new _Future<T>(); new Timer(duration, () { if (computation == null) { result._complete(null as T); } else { try { result._complete(computation()); } catch (e, s) { _completeWithErrorCallback(result, e, s); } } }); return result; }
-
-
延时事件前边如果有耗时事件执行的话,只有等耗时任务执行完毕,它才会被执行。
Future
的基本操作
then
当
Future
中的任务完成后,我们往往需要一个回调,这个回调立即执行,不会被添加到事件队列。
catchError
用来捕获任务异常。
whenComplete
无论任务成功失败,都会回调。
wait
有时候,需要等待多个异步任务都执行结束后才进行一些操作,如有一个界面,需要从两个接口获取数据,获取成功后,将两个数据进行处理后显示在UI界面上,这时候,
Future.wait
派出用上了,它接收一个Future
数组参数,只有数组中所有的Future
执行成功后,就会触发then
回调,当然,只要有一个Future
执行失败就会触发错误回调。
main(List<String> args) {
Future.wait([
Future.value("1.wait"),
Future.delayed(Duration(seconds: 2), () {
return "2.TT";
}),
//3秒后返回结果
Future.delayed(Duration(seconds: 3), () {
return "3.Android";
}),
//4秒后返回结果
Future.delayed(Duration(seconds: 4), () {
return "4.Future";
})
]).then((data) {
//成功逻辑
print(data);
}).catchError((e) {
//捕捉错误
print(e);
}).whenComplete(() {
print('complete~~');
});
}
[1.wait, 2.TT, 3.Android, 4.Future]
complete~~
Exited
any
与
wait
相对,接收参数也是一组Future,表示只要有一个Future完成,则表示成功。
async / await
在
Dart1.9
中加入了async和await关键字,有了这两个关键字,我们可以更简洁的编写异步代码,而不需要调用Future相关的API。他们允许你像写同步代码一样写异步代码和不需要使用Future接口。它仅仅是一个语法糖,简化Future API的使用。
async
:标记某个方法为异步方法(耗时方法),在声明方法的时候使用。
await
:等待某个异步方法<执行完毕>。是“等待”,所以要在调用耗时方法的时候使用。(虽然是等待,但它不是阻塞的。)
分析如下代码输出结果:(future.async.await.dart)
import 'dart:async';
main() {
print("main开始");
_startMethodA();
print("main结束");
}
_startMethodA() async {
print("A执行---begin---");
print(await _startMethodB());
print("A执行---ennnd---");
}
Future<String> _startMethodB() async {
print("B执行---begin---");
String value = await _getData();
return "B执行---ennnd--- 请求到的数据:$value";
}
Future _getData() async {
return Future.delayed(Duration(seconds: 2), () => 'Success, I am B~~~');
}
///
/// 运行结果?
规则、结论
-
Future
中的then
方法,而只是一个普通的Function Call
,在Future
执行完后,立即开始同步
执行 -
当
Future
在then
函数之前已经执行完成
,则会创建一个task,将该task的添加到microtask queue
中,并且该task将会执行通过then
传入的函数 -
then
与Future
函数体共用一个事件循环,如果Future
有多个then
,它们也会按照链式调用的先后顺序同步执行,同样也会共用一个事件循环 -
使用
Future.value
构造函数的时候,就会和第二条一样,创建Task丢到microtask Queue
中执行then
传入的函数 -
Future.sync
构造函数表示同步执行,执行了它传入的函数之后如果还有then
的话,也会立即创建Task丢到microtask Queue
中执行then
会在Future
函数体执行完毕后立刻执行,无论是共用同一个事件循环还是进入下一个微任务。
分析如下代码输出结果:(future.example1.dart/future.example2.dart)
import 'dart:async';
main() {
//普通打印
print('main #1 of 2');
//创建一个微任务
scheduleMicrotask(() => print('microtask #1 of 3'));
//创建一个延迟Future
Future.delayed(Duration(seconds: 1), () => print('future #1 (delayed)'));
//创建一个Future,then链式调用,第二个then中启动一个微任务
Future(() => print('future #2 of 4'))
.then((_) => print('future #2a'))
.then((_) {
print('future #2b');
scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
}).then((_) => print('future #2c'));
//创建一个微任务
scheduleMicrotask(() => print('microtask #2 of 3'));
//创建一个Future,then链式调用,第一个then中返回一个Future
Future(() => print('future #3 of 4'))
.then((_) => Future(() => print('future #3a (a future)')))
.then((_) => print('future #3b'));
//创建一个Future
Future(() => print('future #4 of 4'));
//创建一个微任务
scheduleMicrotask(() => print('microtask #3 of 3'));
//普通打印
print('main #2 of 2');
}
///
/// 输出结果自己思考
import 'dart:async';
main() {
// 声明一个匿名 Future
Future(() => print('f1'));
// 声明 Future fx,其执行体为 null
Future fx = Future(() => null);
// 声明一个匿名 Future,并注册了两个 then。在第一个 then 回调里启动了一个微任务
Future(() => print('f2')).then((_) {
print('f3');
scheduleMicrotask(() => print('f4'));
}).then((_) => print('f5'));
// 声明了一个匿名 Future,并注册了两个 then。第一个 then 是一个 Future
Future(() => print('f6'))
.then((_) => Future(() => print('f7')))
.then((_) => print('f8'));
// 声明了一个匿名 Future
Future(() => print('f9'));
// 往执行体为 null 的 fx 注册了了一个 then
fx.then((_) => print('f10'));
// 启动一个微任务
scheduleMicrotask(() => print('f11'));
// 普通输出
print('f12');
// 声明一个同步的匿名 Future
Future.sync(() => print('f14')).then((value) => print('f15'));
// 声明一个匿名 Future
Future.value('f13').then((value) => print(value));
}
///
/// 输出结果自己思考