[TOC]
Dart基础
- 在Dart中,所有能够使用变量引用的都是对象object,每个对象都是一个类class的实例。数字、函数和
null
都是对象,所有对象继承自Object类,为初始化的变量默认值是null
。 - Dart是强类型语言,但是类型声明是可选的,Dart可以推断类型,无类型则要用
dynamic
类型。 - Dart支持泛型(generic types),如List<int>(整数列表)、list<dynamic>(任意类型列表)。
- Dart支持顶层函数(main()),以及(as well as)与类绑定的函数(分别为:静态和实例化方法),函数可嵌套。
- Dart支持顶层变量,以及与类绑定的(静态和实例)变量,实例变量也成为fields或properties;
- Dart没有
public protected private
,可以在标识前加下划线(_)表示私有。 - 标识可以以字母或下划线(_)开始,后面可以是任意字母、下划线、数字的组合
- Dart支持
表达式expressions
(have runtime value,可返回值)和语句statements
(no runtime value),比如表达式condition?exp1:exp2
会有exp1或exp2中的任一个值,而与if-else就没有runtime value,语句
包含多个表达式
,表达式不能包含语句 - Dart可以报告两种类型的问题:
- warnings:可能工作不正常,但是不会阻止程序运行。
- errors:包括编译错误和运行时错误,编译错误程序无法执行,运行时错误会在运行时发生异常(exception)
代码注释
// 单行注释
/*
*多行注释
*/
/**
*文档注释
*/
变量与常量
Dart定义变量有两种方式:
- 静态类型语言常用的
显示指定变量类型
- 动态指定方式,不指定类型,由vm自动推断。但是一旦指定后就不能再更改类型,如果要更改应当用dynamic 或 Object来定义变量
var
(a way to declare a variable without specifying its type)
常量定义也是两种:final
and const
定义方法是用final或const替换变量前的 var 或 类型指定,final只能set一次,const编译期常量(隐含final),final顶层或类变量在第一次使用前初始化。
- 一是java方法,
final
变量只能赋值一次,运行时常量,运行时确定,其所赋值可以是一个变量,常用于方法。 - 另一种是Dart方式,
const
定义,编译期确定,其所赋值必须是一个字面常量,static const
表示静态常量
final time = new DateTime.now(); //ok
const time = new DateTime.now(); //error
const list = const[1,2,3]; //ok
const list = [1,2,3]; //error
var foo = const[]; // foo 可以修改
const bar = []; // bar 不能修改
foo = [1,2,3]; //ok
bar = [1,2,3]; //error
// Dart2.5以后,可以用类型检查和转换,定义常量
const object i = 3; //i是整数对象
const list = [i as it]; //typecast
const map = {if(i is int)i:'int'}; //use is and collection if
const set = {if(list is List<int>)...list};
注意:
- 实例的变量只能是final,不能是const,final instance varialbles必须在构造函数体前初始化-即在变量声明时、用构造参数初始化,或在构造函数的初始化列表(initializer list)
- 如果在类class中声明const,需要标记为
static const
。
Built-in types
Dart内建数据类型主要包括
- numbers
- strings
- booleans
- lists(类似arrays)
- maps
- runes(在strings中表述Unicode字符)
- symbols
- enumerattions 枚举
- numbers
Dart numbers包括int
和double
两种类型,其中integer类型数据长度不大于64bits,数值从-2^63 to 2^63.double为64bit浮点数据。int
和double
都是num
的子类型(subtype),支持基本的+ - * /
等操作,在int class
中定义了>> << & |
等操作符,其他常用操作在dart:math library
中
//整数定义
var x = 1;
var hex = 0xDEA6;
//浮点定义
var y = 1.1;
var z = 1.42e5;
double z = 1; // 自动转换等同于 double z = 1.0; ver2.1以后
基础工具bsic utilites
parse() //string -> number
toString() //number -> string
toStringAsFixed() //小数位数
toStringAsPrecision() //精度
- Strings
//1.由单双引号创建
var s1 = 'hello';
var s2 = "hello";
//2.多行-三单/双引号
var s3 = ''' 第一行
第二行
''';
//3.原始显示不转义字符串
var s4=r'c:\windows\system32';
//4.拼接字符串
var s = 'hello' + 'world';
var name = 'mike'
var astr = 'hello $name';
var astr2 = 'hello ${name.toUpperCase()}'
// $variableName 或 ${expression}
//5. 比较字符串内容
print('hello' == 'world');
booleans
只有false 和 true两个值,不赋值则为nulllsits
更详细
列表与数组等同,下标从0开始
//1. create
var list1 = [1,2,3]; //infer推论list1 has type List<int>
print(lsit1[0]);
print(list1.length);
list.add(4);
//2. 扩展 扩展操作符spread operator(...)非空判断扩展null-aware spread operator(...?)
var list2 = [7,8,...list1]; //7,8,1,2,3
var list2 = [7,8,...?list1]; //如果list1为null也不会出错,最终返回[7,8]
//3. collection if / for
var list3 = [
'home',
if(true)'Outlet'];
var list4 = [
'home'
for(var i in lsit1)'add$i'
];
//4. create a list's a compile-time constant
var constantList = const [1,2,3];
constantList[1] = 1 ; //error
- Sets
唯一item的无序集合。
var hal = {'ab','cd','1'} // infer hal has the type Set<String>
var hal = <String>{};
Set<String> hal = {};
var hal = {}; //会创建一个map,而不是set
hal.add('x'); // addAll()、length
final constantSet = const {'1','2' }
// 支持 ... 和 ...?扩展,与list类似
- maps
映射又称为关联数组(关联keys 和 valuse),相当于java中的HashMap,keys 和 valuses能是任意类型的对象object。key只能出现一次,valuse可以重复。
// 字面创建
var gits = {
'first': '1',
'second': '2'
}; // gits has type Map<String,String>
// 构造函数创建
var mp1 = new Map(); // var mp1 = Map(); 在Dart中鼓励不用new
mp1['first'] = '1';
mp1['second'] = '2';
print(mp1.length);
print(mp1['first']); //如果不存在返回null
//常量
final mp = const {1:'a',2:'b'}; //const 可省略
常用函数
length
keys
values
containsKey
containsValue
remove
forEach
map.values.toList()
- 枚举类型
枚举类型称为enumerattions或enums,代表固定数量的常量特殊类型。
enum Color{
red,
green,
blue
}
assert(Color.red.index == 0) ; //枚举类型中每个值都有一个index getter方法,返回index从0开始。
//获取所有值,用values
List<Color> colors = color.values;
函数
dart中,函数(方法)也是对象,类型是Function,可以赋值给变量,也可以作为其他函数的参数。可以像调用函数一样调用一个类的实例。
定义函数
String greet(String name){
return 'hello $name';
} //类型string 可省略 effective dart不建议
函数类型:函数类型是可选的,可以省略显示类型(建议显示指定),
返回值:void
修饰时函数时无返回值,未指定时返回null。
=>操作符
函数中只有一条表达式时候可以
String greet(String name)=>“hello $name”·;
=>expr 是简写了{return expr;}
注意只有一条表达式expression可用此操作符,而不是一条语句statement(如if-else)
可选参数利用可选参数可实现类似重载(dart中没有重载)
可选参数可以是命名参数 或是 位置参数,函数定义可选参数只能用其中一种。
- 命名参数:调用时用
ParaName:value
指明命名的参数。定义时候用{param1,param2 ..}
指定命名参数,对于可选参数可以注释为@required
要求调用时必须提供参数。
void enableFlags({int bold, int hidden}){ ... }
void Scrollbar({Key key, @required Widget child}){ ... }
- 位置可选参数:用方括号[ ]包裹
String say(String from, String msg, [String device]){
var result = '$from say $msg';
if(device != null){
result = '$result with a $device';
}
}
默认值可以用=
定义命名或位置参数默认值,默认值要是编译期常量(compile-constants),如果没有定义那么缺省值为null
。
void enableFlags({bool bold = false,bool hidden = false}){ ... }
void doStuff({List<int> lst=const[1,2,3],Mpa<int,String>gifts = const{1:'1',2:'2'}}
{...}
主函数main()
主函数是程序的入口,有一个可选参数List<String>,无返回值
void main(List<String> arguments) { ... }
函数作为参数传递
//传递给函数
void printElement(int element){print(element);} //void printElement(int element)=>print(element) ?????
var list = [1,2,3];
list.forEach(printElement);
//传递给变量 使用了匿名函数
var loudify = (msg) => "message is:$msg";
print(loudify('hello'));
匿名函数anonymous functions
可以像lambda和closure一样使用匿名函数,与命名的函数类似,可以没有或有多个参数:
([Type] param1[,...]){ codeBlock }
var lst = ['1','2','3'];
lst.forEach((item){
print('${lst.indexOf(item)}is:$item')
}); //lst.forEach((item)=>print('${lst.indexOf(item)}is:$item'));
闭包 lexical closures
闭包是一个函数对象function object,可以在原始范围外访问内部变量。并可以持久记住其状态。
Function makeAdder(num addBy){
return (num i)=>addBy+i; // 返回一个函数对象,addBy会被返回的函数记住状态
}
void main(){
var add2 = makeAdder(2);
var add3 = makeAdder(3);
add2(5); //7
add3(5); //8
//==========
a() {
int count = 0;
printCount(){
print($count++);
}
return printCount();
}
void main(){
f = a();
for (i=0;i<=5;i++){
f();
} // output:0 1 2 3 4 5
}
}
操作符
- 比较操作符
== != > < >= <=
- 算术运算符
+ - -exp(负号) * / ~/(整除) %(余数modulo) ++ --
- 类型测试符
as(typecast)、 is 、is!(not is)
- 赋值操作符
= 、??= b??=a //assign value to b if b is null;otherwise b stays the same
- 复合赋值
= -= /= %= >>= ^= += *= ~/= <<= &= |=
a op= b 即 a=a op b - 逻辑运算
!expr(not) ||(or) &&(and)
- 位操作符
& | ^ ~ << >>
- 条件表达式
condition ? expr1 : expr2 // if condition is true reuslt expr1 else reult expr2 ; expr1 ?? expr2 //if expr1 is not null result expr1 else result expr2
- 级联符号
..
允许在一个对象上执行一些列操作:
querySelector('#confirm') //get an object
..text = 'Confirm'
..classes.add('importand')
..onClick.listen((e)=>window.alert('Confirmed'));
//等同于da
var btn = querySelector('#confirm'); //get an object
btn.text = 'Confirm'
btn.classes.add('importand')
btn.onClick.listen((e)=>window.alert('Confirmed'));
//可以嵌套
final addressBook = (AddressBookBuilder()
..name = 'mike'
..email = 'x@xxx.com'
..phone = (phoneBuilder()
..number = '1234556'
..lable = 'home')
.build()) //phoneBuilder().build()
.build(); //addressBookBuiler().bulild()
- 其他操作符
( ) 函数调用
[ ] 列表访问
. 成员访问 foo.bar
?. 条件成员访问 foo?.bar foo非空时访问foo.bar
流程控制语句
- if-else
- for loops
- while and do-while
- brak and continue
- switch and case
- assert
if (isRaining){
you.bringRainCoat();
} else if (isSnowing()){
you.wearJacket();
} else {
car.putTopDown();
}
var callbacks = [];
for (var i=0; i<=2; i++){
callbacks.add(()=>print(i));
}
callbacks.forEach((c)=>c());
while(!isDone()){
doSomething();
}
do{
dosomethine();
}while(!isDone());
var command = 'OPEN';
switch(command){
case 'CLOSED':
executeClosed();
break; //break如果缺少了会报错
case 'OTHER':
executeOther();
continue now; //跳转到now标签
case 'OPEN':
executeOpen();
break;
now: // a label name
case 'NOW_CLOSED': //empty case ok
default:
executeUnknown();
}
// for 循环
for (int i=0;i<=100;i++){
;
}
for (String item in aList){
;
}
int i=0;
while(i<10){
;
}
do {
;
}while(i<=10)
//assert(condition, opionalMessage)
//在开发中,使用assert语句,当条件false中断,显示默认错误提示或指定错误提示
assert(number<100);
assert(urlString.startsWith('https'),'shold start with "https".');
#### Exceptions ####
未预见到的错误发生如果没有捕捉处理,出现例外的isolate线程会挂起suspend,一般线程和其程序会终止。
throw exception `throw FormatException('Excepted')`,可以抛出任意对象 `throw 'Out of'`
```dart
try{ breedMorelamas();
}on outOfLlamasException{... } // 指定例外值
on Exception catch(e){ // 捕捉exception
print('Unknown exception $e');
}
catch(e) {... } //不指定类型,捕捉所有异常
try { }
cathch(e) { }
finally { }
Class
Dart是有类class和混合继承(mixin-based inheritance)的面向对象的编程语言,所有对象object都是一个类的实例,所有类派生自Object类,基于混合继承意味着尽管每个类都只有一个超类(superclass)一个类主体可以在多个类继承中重用,Extension methods能在不改变类和创建子类的情况下添加功能。
- 引用类成员
引用类成员 p.y = 3; p?.y=4 //if p is not-null set its y=4
- 使用构造函数
类的构造函数可以是ClassName或者ClassName.identifier
var p1 = Point(1,2);
var p1 = Point.fromJson({'x':1.'y':2});
//获取类型
print(p.runtimeType);
- 类的变量 instance variables
未初始化的变量为null,所有实例变量都有隐含的 getter 和 setter(final instance除外)。
在声明变量时初始化,变量值会在创建实例时设置,早于构造函数和初始化列表执行。 - 构造函数
构造函数是与类同名的函数(可以加上标识符,可以是ClassName或者ClassName.identifier)
Dart构造函数有四种格式:
className() //普通构造函数
className.identifier() // 命名构造函数
const className() //常量构造函数
factory className() //工厂构造函数
class Point{
num x, y;
point(num x,num y){
this.x = x;
this.y = y;
} // 此种赋值类构造函数更好的方法是用模板 point(this.x, this.y);
}
如果没有声明构造函数,类会提供缺省的构造函数,缺省构造函数没有参数直接调用超类的无参数构造函数。
子类不会继承超类的构造函数,子类不声明构造函数仅有一个缺省的无参数无名称的构造函数。
命名构造函数使用命名构造函数可以为类实现多个构造函数,或使功能更清晰。
class Point{
num x, y;
pont(this.x,this.y);
//named constructor
point.origin(){
this.x = 0;
this.y = 0;
} //原点构造函数;
}
注意由于构造函数不会继承,因此命名的构造函数在子类中没有,需要重新构建。
构造函数的执行顺序initializer list -> superclass's no-arg constructor -> main class's no-arg constructor
.
如果超类没有unnamed no-argument的构造函数,必须手工调用一个构造函数,方式为subclassConstructor : supclassConstructor.identifer
- 初始化列表 initializer list
Pont.fromJson(Map<string,num> json)
: x = json['x'],
y = json['y'],
distance = sqrt(x*x+y*y){
print('body runing');
}
- 重定向构造函数
使用现有构造函数可以解决问题
class Point{
num x,y;
point(this.x,this.y);
pont.alongXAxis(num x):point(x,0);
}
- Factory constructor
使用factory
关键字声明构造函数不只是返回新实例,可以返回缓存的实例,工厂函数不能使用this。工厂函数就是可以批量产生对象的函数,可以返回一个实例,而不用类的方法。
class Logger{
final String name;
bool mute = false;
static final Map<String,Logger> _cache = <String, Logger>{};
factory Logger(String name){
return _cache.putIfAbsent(name, ()=>Logger._internal(name));
}
Logger._internal(this.name);
void log(String msg){if (!mute) print(msg);}
}
main(){
var logger = Logger('UI'); //new
var logger1 = Logger('Button');//new
var logger3 = Logger('UI');//return cache = logger
}
如果类创建的对象不变,可以在编译期创建这个常量实例,并使用常量构造函数,确保所成员都是final
class p{
final num x,y;
const p(this.x,this.y);
}
- 方法 Method
getter和setter所有实例变成员量都有隐含的 getter 和 setter(final instance除外)。但是也可以通过set
和get
关键字来声明相应的方法,执行自定义操作
class Rectangle{
num left,top,width,height;
Rectangle(this.left,this.top,this.width,this.height);
num get right=>left + width;
set right(num value)=>left = value - width;
}
抽象方法方法可以是抽象方法,定义一个接口让其他类去实现,抽象方法只能存在于抽象类中。
抽象类不能被实例化,一般用来定义接口声明,如果要实例化抽象类,要使用工厂构造函数。
abstract class Doer{
void doSomething(); //创建抽象方法,用;代替方法体
}
class EffectiveDoer extends Doer{
void doSomething(){ ... }
}
隐式接口在Dart中,类和接口是统一的,每个类都隐式定义一个接口,其中包含该类成员(变量、方法)及所有声明的接口,如果想创建一个支持B类API的A类,则A类应该实现B接口。类通过在implements
字句中声明一个或多个接口,然后提供接口所需的API来实现一个或多个接口。
如果有一个类A,想让类B拥有A的API,但又不想有用A中的实现,那么可以把A当作接口,用类B来implements 类A。class就是interface 当类被当作interface的时候,类中的方法就是接口的方法,需要在子类中重新实现,在子类实现的时候用@override,成员变量也要重新实现,用@override,实现接口可以有多个
如果是复用已有类的实现,使用继承extends
,如果只是使用已有类的外在行为,使用接口implements
。
继承是单继承 ,构造函数不能继承;子类重写超类的方法 用@override; 子类调用超类的方法用super。
class Person{
final _name; // 在接口中,但仅本库可见
Person(this._name); //不在接口中,是构造函数
String greet(String who)=>'Hello,$who. I am $_name.'; //在接口中
}
//person接口的实现
class Impostor implements person{
get _name=>'';
String greet(String who)=>'Hi $who.Do you know who am i?';
}
String greetBob(Person person)=>person.greet('Bob');
void main(){
print(greetBob(Person('Mike'));
print(greetBob(Impostor());
}
- 接口
不管是抽象类还是实例类都可以用来实现接口,建议使用抽象类来实现接口,因为实现 实现类里的方法和属性,如果不使用抽象类,显得乱。
接口一般用来定义统一的操作规范。比如定义一个数据库的操作类,操作方法都一样 包括 add save delete;
接口实现类包括操作mysql mssql mongodb等
abstract class DB{
String uri;
add(String data);
save();
delete();
}
class Mysql implements DB{
@override // 覆盖父类实例方法。
String uri
Mysql(this.uri);
@override
add(data){ ...}
@override
delete(){...}
@override
save(){ ... }
remove(){ .. } //新增的
}
- Mixin
多个类层次重用代码,用法为class AB extends P with A,B
,会组合一个新类顺序为PAB。是一种线性化的,创建实例var ab = AB()
,则ab is p ,ab is A,ab is B
均为true,因为每个mixin应用都创建一个新类,还会创建一个接口,新类中扩展了超类并包含mixin类成员的副本,也实现了mixin类的接口。
P-PA-PAB-->ab; 创建mixin类,然后将代码‘混入’扩展类中。
abstract class Animal{ // 动物的通用属性
}
// 跑的动作实现
mixin Run{
run(){print('run');}
}
// 飞的动作实现
mixin fly{
fly(){print('fly');}
}
// 游的动作实现
mixin Swim{
swim(){print('swim');}
}
void main(){
class Bird extends Animal with Fly{} //会飞的鸟
calss Dog extends Animal with Run{ } // 会跑的狗
class Frog extends Animal with Run,Swim{ } //会跑 会游的蛙
Bird b = Bird();
b.fly();
Frog f = Frog();
f.run();
f.swim();
}
- 操作符重载
class Vector{
final int x,y;
Vector(this.x,this.y);
Vector operator +(Vector V)=>Vector(x + v.x, y + v.y);
}
- noSuchMethod()
在代码中尝试使用不存在的方法或变量时,作为响应,可以重写noSuchMethod()
@overrid void noSuchMethod(Invocation invocation){print(no ${invocation.memberName});}
类的方法和变量
- 用static实现类的变量和方法。
- 静态变量只有在使用时才被初始化,静态方法不在实例上进行操作,不必访问this。
- 类的方法和变量只能通过类访问,不能通过实例不能访问。
泛型 Generics
一般情况下类或函数都需要确定返回的类型,有时在需要在使用时确定类型。
泛型可以更安全:尽管可以通过继承实现以上要求,但泛型在编译期会进行类型检查,而不是在运行期。
可以快速创建复杂的类型,由于在编写时没有指定类型,因此可以在使用时随意指定类型。
自动完成类型转换。没有泛型时,如果不能确定返回类型,那么就需要返回Objcect,有了泛型就可以确定具体类型。
泛型-有形参的类型,是可以被参数化的类型。通常为了类型安全(type safety)使用泛型,另外可以写出更好的代码,减少代码重复。
如List<String>
读作list of string。
abstract class Cache<T>{
T getByKey(String key);
void setBykey(String key,T value);
}
//否则就要写成
// int 类
abstract class CacheInt{
int getByKey(String key);
void setBykey(String key,int value);
// string 类
abstract class CacheString{
int getByKey(String key);
void setBykey(String key,String value);
}
//泛型类型初始化使用只需要使用<Tyupe>或<keyType,valueType>即可。
var names = <String>['1','2']; //lsits
var set = <String>{'1','2'}; //sets
//如果使用构造函数初始化
var names = List<String>();
var names = Set<String>.from(names);
//泛型类
class PrintClass<T>{
List list = new List<T>();
void add(T value)=>this.list.add(value);
void printInfo(){ .. }
}
//泛型方法
//泛型最初主要用于支持类,现在可以支持泛型方法。
//T 可以用在三个地方: 1 函数返回类型 2 参数类型 3 局部变量
T first<T>(List<T> ts){
...
T tmp = ts[0];
return tmp;
}
// 限定参数范围 T extends classname
T sumPair<T extends num>(List<T> items){
return T items[0]+items[1];
}
//var ls = <double>[1.2,3.5];
//var b = sumPair(<int>[1,2]); //=3
//var c = sumPair(ls); //=4.7
库和可见性
import和库指令可以帮助创建模块化且可共享的代码库。库不仅提供API,而且是隐藏细节的方法,用_
开头的标识仅库内部,可见。每个Dart程序都是一个库,即使它不使用库指令。
- 可以使用packages分发库。
- 使用库命名空间
import 'dart:html'
。dart内建库标识dart:
其他库可以使用 路径 或package:
package指定用一个包管理器作为库提供者。imprt 'package:test/test.dart'
。 - 指定库前缀
import 'package:lib2/lib2.dart' as lib2
使用`lib2.Element el=lib2.Element() - 导入库的部分内容:导入
show
,隐藏hide
例如:import 'package:lib1/lib1.dart' show foo;
- 后期导入库
deferred
import 'package:greetings/hello.dart' deferred as hello;
Future greet() async{
await hello.loadLibrary(); //即使多次调用,也只会载入一次
hello.printGreeting();
}
异步Asynchrony support
async & await
在Dart中没有子线程一说,所有代码都是在一条主线上运行,比较耗时的操作用异步来实现。
async
用来修饰方法,卸载方法的括号之后,await
写在方法内部,但是await 关键字必须在async函数内部使用,不然会报错,await表达式可以使用多次。即使是在main()中使用await main()也要标记async
Future main() async{
....0 await for(var request in requestServer){
.....
}
}
void main(){
getName1();
getName2();
getName3();
}
getName1() async {
await getStr1();
await getStr2();
print('getName1');
}
getStr1(){
print('getStr1');
}
getStr2(){
print('getStr2');
}
getName2(){
print('getName2')
}
getName3(){
print('getName3')
}
执行顺序 main - getName1 - getstr1(遇到await会先执行这一行,之后立即返回future(void)) -
执行顺序 | 执行结果 | 说明 |
---|---|---|
main() | 主函数执行 | |
getName1() | 第一个函数 | |
getStr1() | getStr1 |
遇到await会先执行这一行,之后立即返回future(void),然后将剩余方法放入事件队列(即getStr2()和print('getName1')) |
getName2() | getName2 |
|
getName3() | getName3 |
主程序执行完毕 |
getStr2() | getStr2 |
队列中还有未执行 |
print('getName1') |
** then, catchError, whenComplete**
async方法遇到await会返回一个future对象,是一个未来值,完成之后才能取出。then关键字就是获取等待完成之后的返回值。
void main(){
new future(() => futureTask()) //异步执行函数
.then((i) => "result is $i") //任务执行后的子任务
.then((m) => print(m)) //m为上个任务执行后的返回值 “reulst is 10”
.then((_) => Future.error('出错了')) // 可以抛出一个异常
.catchError(print) //捕捉异常,然后执行代码,print
.whenComplete(()=>print("complete")); //左右任务执行完成后 执行的回调函数
}
futureTask(){
return 10;
}
在Dart的main isolate中只有一个Event looper,维护两个队列,即事件队列Event Queue,和Microtask Queue不断从消息队列中取出消息并处理它们,消息可以是用户输入、点击、定时器等。
同时main isolate还维护一个MicroTask Queue,对已经执行的事件,再次执行then,就会放入微队列 **main线程执行完毕,再看事件队列,执行事件队列之前需要检查 微队列中是否有东西,如果微队列中有就需要先执行 **
schedureMicrotask()
可以主动将代码放入微队列中
void main(){
testSchduleMicrotask();
}
void testSceduleMicrotask(){
scheduleMicrotassk(()=>print('s1')); //放入微队列
new Future.delayed(new Duration(seconds:1), ()=>print('2')); //延时直接放入事件队列最后
new Future(()=>print('s3')).then((_){print('s4');scheduleMicrotask(()=>print('s5'));}
print('s6')
}
主循环 s6
事件队列 s3(s4)、s2
微队列 s1、s5
- 尽量将任务放入event事件队列中。
- 使用Future的then方法或whenComplete方法来指定任务顺序。
- 为了保持响应性,尽量不要将大量计算放入这两个队列。
- 大量计算任务放入额外的isolate中(另外的线程)
生成器
yield 在迭代生成器中返回序列值中的一个值。有时候需要生成一个序列,并循环处理每个item,如果使用列表,当元素很多时候,会占用大量内存,如果用生成器每次会返回一个值,而不会返回整个列表,并记住位置,下一次返回next。因此yield 相当于生成器函数中的return。
包括
- 同步生成器 返回
Iterable object
- 异步生成器 返回
Stream object
//如生成斐波那契数列
Iterable<int> Fab(int max) sync*{
int n=0;
int a=0;
int b=1;
int t=0;
while(n<max){
yield b;
t=a;
a = b;
b = t + b;
n++;
}
}
Stream<int> Fab(int max) async*{
int n=0;
int a=0;
int b=1;
while(n<max){
yield b;
t=a;
a = b;
b = t + b;
n++;
}
}
void main(){
for (item in Fab(10)){
print(item);
}
}
//对于递归生成器,递归前用 yield *提高性能
Iterable<int> DownFrom(int n) sync* {
if (n > 0){
yield n;
yield * DownFrom(n-1);
}
}
函数类型定义
typedef
目前仅用于定义函数类型
typedef Compare = int Function(Object a, Object b);
//typedef Compare<T> = sort(T a,T b);
class SortedCollection{
Compare compare;
SortedCollection(this.compare);
}
int sort(Object a,Object b)=>0;
//int sort(int a, int b)=>a-b;
void main(){
SortedCollection coll = SortedCollection(sort);
....
}
元数据Metadata####
元数据给代码附加更多信息,元数据以@
开始