文章目录
编译时常量
字面量(如数字、bool、字符串、List的字面量形式)、其它常量或者常量的算术运算,也可以是这些的组合形式,简单地说常量就是可以在编译时确定的值
//编译时常量
void test2(){
const a =1;
const b=true;
const c=a;
const d=2*3;
const e= a*d +c;
}
关于const 与 final
- 区别一:final要求变量只能初始化一次,并不要求赋的值一定是编译时常量。而const要求在声明时初始化,并且赋值必需为编译时常量。
- 区别二:final是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了,以下的const和final没有区别、
void test1(){
var a1 = 1;
const a2 =1;
final a3= 1;
const int a4= 1;
final int a5 =1;
}
如果实例化一个新的对象,一个函数调用的返回值就不是编译时常量了,它们在运行时才能获得结果。
void test3() {
final x1 = new DateTime.now(); //ok
const x2 = new DateTime.now();//error
}
final实例成员的用法
//声明时直接初始化
class X1 {
final a = 0;
}
//或构造方法中初始化
class X2 {
final a;
X2(this.a){
}
}
//或初始化列表中初始化
class X3 {
final a;
X3(a): this.a = a{
}
}
const实例成员的用法
class X {
//只能声明时直接初始化
static const a = 0;//必须是static的
X(){
}
}
常量对象和常量构造函数
- 所有实例成员都是 final 或 const 实例成员,那么该类所创建的对象的状态就是不可变的。
- 定义 const 构造函数,const 构造函数所创建的对象就是常量对象。
- 用 const 代替 new 来调用构造函数。
- 可以用 new 调用 const 构造函数,但那样就不能用 const 声明该变量了。
- const 构造函数也不能有函数体。
- 常量对象还有一个特点,相同的常量对象始终只有一个。
class Constant {
static const a = 1;
final b;
const Constant(this.b); //构造函数前加const,不能有函数体
static const m = const Constant(3);
}
main(){
const x1 = const Constant(3);// ok
const x2 = new Constant(3);// error
final x3 = const Constant(3);// ok
final x4 = new Constant(5);// ok
assert(x1 == x3);
assert(x1 != x4);
}
class X1{
final a =0;
}
class X2{
final a;
X2(this.a);
}
class X3{
final a;
X3(a) : a = a {}
}
class X {
static const a = 0;
}
const 可以修饰变量,也可以修饰值,而 final 只用来修饰变量。
void test4(){
const list1 = [1, 2, 3]; // error
const list2 = const [1, 2, 3];
const list3 = const [new DateTime.now(), 2, 3]; // error, 因为new DateTime.now()不是const
const list4 = const [const Constant(3), 2, 3];
var x1 = 5;
const list5 = const[x1]; //error,x1不是const
const x2 = 5;
const list6 = const[x2];
final list7 = [1, 2, 3];
final list8 = const [1, 2, 3];
final list9 = const [new DateTime.now(), 2, 3];// error, 因为new DateTime.now()不是const
}
const 修饰值的时候,要求值必需是常量或由常量组成。var、final等在左边定义变量时,并不关心右边是不是常量。但如果右边用了const,那么不管左边是什么要求,右边都必需是常量。
异步
Async和await
Future getName1() async {
await getStr1();
print('getName1');
}
getStr1() {
print('getStr1');
}
getName2() {
print('getName2');
}
getName3() {
print('getName3');
}
goAsync() {
getName1();
getName2();
getName3();
}
//输出结果
getStr1
getName2
getName3
getName1
- return的类型为T,那么函数的返回类型应该为Future,或T的父类,否则会产生静态警告。
- 如果函数中return的类型是Future,那么函数的返回类型同样为Future,而不是Future<Future>。
- await表达式可以使用多次,但只能在async标记的函数中使用。
Future
Future表示在将来某时获取一个值的方式。当一个返回Future的函数被调用的时候,做了两件事情:
- 函数把自己放入队列和返回一个未完成的Future对象
- 之后当值可用时,Future带着值变成完成状态。
//1.使用then按指定顺序
getName2().then((_) => getName1()).then((_) => getName3());
//2.使用whenComplete()按指定顺序
getName2().whenComplete(() {
getName1().whenComplete(() {
getName3();
});
});
//3.使用wait,一并返回完成值
Future.wait([getName2(), getName1(), getName3()])
.then((List re) {
re.forEach((i) => print(i));
});
Future与异常
//返回都是Future可以用链式结构
void goThrow() {
new Future(() => print('start'))
.then((_) => new Future.error("xxxxxx"))
.whenComplete(() => print("run"))
.then((_) => print("don't"))
.catchError((e) => print(e));
}
//混合同步类型和异步类型的异常
main() {
fun3().catchError((e){
print('e = $e');
});
}
fun1() {
throw 'fun1 is error';
}
fun2() {
throw 'fun2 is error';
}
//方法1:
//Future fun3(){
// return new Future.sync((){
// fun1();
// return new Future(() {
// fun2();
// });
// });
//}
//方法2:
Future fun3() async{
fun1();
return new Future(() {
fun2();
});
}
事件队列
- Dart应用程序仅有一个消息循环,以及两个队列:event事件队列和microtask微任务队列。
- 事件队列包含所有的外部事件,如:I/O、鼠标事件、定时器、Isolate之间的消息等等。
当main函数退出后,消息循环开始工作,首先FIFO的方式执行所有微任务。接着,从事件队列中提取消息并一条条处理,然后再执行所有的微任务,以此循环,直到所有队列为空。
如何调度任务
1. Future类,可添加一个事件到事件队列的末尾;
2. 顶层函数 scheduleMicrotask(),可添加一个任务到微任务队列的末尾。
Future几点注意事项:
1. 当Future完成计算后,then()注册的回调函数会立即执行。需注意的是,then()注册的函数并不会添加到事件队列中,回调函数只是在事件循环中任务完成后被调用。
2. 如果Future在then()被调用之前已经完成计算,那么任务会被添加到微任务队列中,并且该任务会执行then()中注册的回调函数。
3. Future()和Future.delayed()构造函数并不会立即完成计算。
4. Future.value()构造函数在微任务中完成计算,其他类似第2条。
5. Future.sync()构造函数会立即执行函数,并在微任务中完成计算,其他类似第2条。
import 'dart:async';
main() async {
Future f1 = new Future(() => null);
Future f2 = new Future(() => null);
Future f3 = new Future(() => null);
f3.then((_) => print("1"));
f2.then((_) {
print("2");
new Future(() => print("3"));
f1.then((_) {
print("4");
});
});
}
//运行结果
2
4
1
3
import 'dart:async';
main() {
print('1');
scheduleMicrotask(() => print('s1'));
new Future.delayed(new Duration(seconds: 1), () => print('d1'));
new Future(() => print('f1')).then((_) {
print('f2');
scheduleMicrotask(() => print('s3'));
}).then((_) => print('f3'));
new Future(() => print('f4'));
scheduleMicrotask(() => print('s2'));
print('2');
}
//打印结果:
1
2
s1
s2
f1
f2
f3
s3
f4
d1
先循环main函数,然后是微任务队列,然后是事件队列,如果事件队列中还有微任务,那就执行完当然的事件队列再接着执行微任务队列,微任务结束后再执行事件队列,以此循环。
ps:
- 尽量使用事件队列,微任务要尽量简单,否则会引起鼠标无反应等。
- 为使应用程序保持响应,避免在事件循环中添加计算密集型代码
- 执行计算密集型代码的时候,另创建Isolate
生成器
同步生成器:sync*
Iterable getNum(n) sync* {
print("Begin");
int k = 0;
while (k < n) {
//moveNext会返回true给调用者。
//函数会在下次调用moveNext的时候恢复执行。
yield k++;
}
print("End");
}
goIterable() {
//调用getNun立即返回Iterable
var it = getNum(3).iterator;
//调用moveNext方法时getNum才开始执行
while(it.moveNext()) {
print(it.current);
}
print('over');
}
//打印结果
Begin
0
1
2
End
over
异步生成器:async*
Stream getNum(n) async* {
print("Begin");
int k = 0;
while (k < n) {
//不用暂停,数据流通过StreamSubscription进行控制
yield k++;
}
print("End");
}
goStream() {
//调用getNum立即返回Stream,只有执行了listen,函数才会开始执行
getNum(3).listen((v) {
print(v);
});
print('over');
}
//打印结果
over
Begin
0
1
2
End