Dart语言特性

Dart语言特性

变量声明

  • var 可以接收任何类型的变量,一旦赋值,就不能改变其变量类型
1
2
var a = 100;
a = "100"; //此时会报错,dart根据编译时第一次赋值类型,推断出变量类型,编译结束后,该变量类型遂即确定,不能再改变
  • dynamicObject
      * Object是所有对象的基类,所有类型都是Object的子类,包括函数(Function)和Null, 所以任何类型的数据都可以赋值给Object声明的对象。
      * dynamicvar一样,也可以接收任何类型的变量
      * 注意:dynamicObject赋值后,后期可以改变其变量类型
  • finalconst
      * 被finalconst修饰的变量,变量类型可以省略
      * const是编译时常量,而final是在第一次使用时,被赋值

数据类型

  • 数值类型
变量关键字 说明
int 整数类型
double 浮点类型
1
2
3
4
5
6
7
void main() {
int a = 10;
double d = 15.0;
print("a: " + a.toString() + ", d: " + d.toString());
}
输出:
a: 10, d: 15.0
  • 字符串类型
1
2
3
4
5
6
7
void main() {
String s1 = "s1";
String s2 = "s2";
print(s1 +" " + s2);
}
输出:
s1 s2
  • 布尔类型
1
2
3
4
5
6
void main() {
bool b = true;
print(b);
}
输出:
true
  • 数组类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void main() {
 List list = new List();//声明方法1(默认dynamic类型)
 list.add("value");
list.add(123);
list.add(true);
print(list);
 List list1 = new List<int>();//限制为int类型后,就只能添加int类型
 list1.add(1);
print(list1);
 List list2 = [1,2,3,4,5];//声明方法2
 print(list2);
}
输出:
[value, 123, true]
[1]
[1, 2, 3, 4, 5]
  • 字典类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void main() {
var map = {
"name": "Joy",
"age": 12,
"sex": "男"
 };//声明方式1
 print(map);
print(map["name"]);
 var map1 = new Map();//声明方式2
 map1["name"] = "Tom";
map1["list"] = [1, 2, 3, 4, 5];
print(map1);
}
输出:
{name: Joy, age: 12, sex: 男}
Joy
{name: Tom, list: [1, 2, 3, 4, 5]}

函数

Dart是一种真正的面向对象的语言,所以函数(Function)也是对象。因此函数可以作为参数传递给其他函数或者赋值给变量。

  • 函数没有显式返回值时,默认返回dynamic类型。例如
    1
    2
    3
    bool isToday(long mills) {

    }
  • 函数返回值没有类型推断
  • 对于只包含一个函数的表达式,可以是用简写方式

bool isNumOne => number == 1;

  • 函数作为一个变量,例如

    1
    2
    3
    4
    5
    6
    7
    void main() {
    eat("apple");
    }
    var eat = (str) {
    print("eat $str");
    };
     输出:eat apple
  • 定义函数时,函数可以添加一组可选的位置参数(即该参数可有可无)
     1. 将可选参数放在函数参数列表的最后位置
     2. 在中括号[]添加一组参数
    ,例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
speakWord("Tom", "hello");
speakWord("Tom", "hello", 26, "shanghai");
}

void speakWord (String name, String msg, [int age, String where]){
if(age != null && null != where){
print("$name speak $msg at $where and age is $age");
} else {
print("$name speak $msg");
}
}
 输出:
Tom speak hello
Tom speak hello at shanghai and age is 26
  • 定义函数时,可以指定命名参数
     1. 将指定的命名参数放到函数参数列表的最后位置
     2. 在大括号中指定一组命名参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void main() {
speakWord("Tom", where: "shanghai");
speakWord("Tom");
}

void speakWord (String name, {int age, String where}){
if(where != null and age != null){
print("$name at $where");
} else {
print("name is $name");
}
}
 输出:
Tom at shanghai
name is Tom

注意: 指定命名参数和可选位置参数,不能同时使用

异步支持

Dart有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数。在Dart中async和await关键字支持了异步编程,因此可以写出和同步代码很像的异步代码。

Future

Future表示一个结果的最终完成,要么成功,要么失败,永远只返回一个结果。Future的调用结果还是一个Future对象,因此很方便进行链式调用。

Future当中,常用的API有以下几种

  • Future.then

      Future执行异步任务后会在then中接收结果。then函数的定义如下:

    Future<R> then<R>(FutureOr<R> onValue(T value), {Function? onError});

      其实可以看到then函数中定义了一个可选参数 onError,源码中建议then函数中只使用onValue的回调,而错误应该使用catchError函数

      例如

    1
    2
    3
    4
    5
    6
    7
    void main() {
    Future.delayed(new Duration(seconds: 2), () {
    return "Hello World";
    }).then((value) => print("result : $value"));
    }
       输出
    result : Hello World
  • Future.catchError

      Future执行任务遇到错误时,则catchError会处理错误。catchError
     函数定义如下:

    Future<T> catchError(Function onError, {bool test(Object error)?});

      如果then函数中已经添加了onError回调,则不再回调catchError函数。例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void main() {
    Future.delayed(new Duration(seconds: 2), () {
    throw UnimplementedError("UnimplementedError");
    }).then((value) => {print("result : $value")}, onError: (e) {
    print("onError invoke");
    }).catchError(() => print("catchError invoke"), );
    }
       输出:
    onError invoke
  • Future.whenComplete有时候无论回调成功或者失败,都需要回调,就可以用whenComplete函数

    例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      void main() {
    Future.delayed(new Duration(seconds: 2), () {
    throw UnimplementedError("UnimplementedError");
    })
    .then((value) => {print("result : $value")}, onError: (e) {
    print("onError invoke");
    })
    .catchError(
    () => print("catchError invoke"),
    )
    .whenComplete(() => print("whenComplete invoke"));
    }
       输出
      onError invoke
    whenComplete invoke

     

  • Future.wait等待所有的异步函数执行完成后 在执行一些操作,可以使用wait函数。Future.wait接收一组Future数组参数,当所有的异步操作完成后,才执行then函数(这点其实类似于RxJavazip操作符号)。当其中一个操作失败后,就会触发错误回调(onError函数或者catchError)。

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void main() {
    Future.wait([
    Future.delayed(Duration(seconds: 3), () {
    return "Hello";
    }),
    Future.delayed(Duration(seconds: 5), () {
    return "World";
    })
    ]).then((value) => print(value));
    }
       输出
    [Hello, World]

      抛出错误例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    void main() {
    Future.wait([
    Future.delayed(Duration(seconds: 3), () {
    return "Hello";
    }),
    Future.delayed(Duration(seconds: 5), () {
    throw Exception("throw execption");
    })
    ]).then((value) => print(value)).catchError((error) {
    print(error);
    });
    }
       输出:
    Exception: throw execption

避免回调地狱问题(Future、async/await

Dart中可以有两种方式解决回调地狱问题,即Future对象和 async/await

  • 利用Future API的特性

    Future执行完成后依然返回Future对象,所有很方便的进行链式调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    void main() {
    getToken().then((token) {
    return getUserInfo(token);
    }).then((user) {
    return saveUserInfo(user);
    }).then((value) => print("result $value"));
    }

    Future getToken() {
    return Future.delayed(Duration(seconds: 2), () {
    return "token";
    });
    }

    Future getUserInfo(String token) {
    return Future.delayed(Duration(seconds: 2), () {
    return UserInfo();
    });
    }

    Future saveUserInfo(UserInfo info) {
    return Future.delayed(Duration(seconds: 1), () {
    return true;
    });
    }
       输出
    result true
  • async/await

    asyncawait是为了解决回调地狱问题,也就是让写异步代码就像写同步代码一样,逻辑看起来更清晰。

      注意:

      - async表示函数是异步的,返回的是一个Future对象。可以通过then函数获取结果回调
      - await后面是一个Future函数,表示需要等待该函数执行完成后,才继续执行下一步
      - await必须出现在async函数内部

 
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void main() {
getAndSaveUserInfo().then((value) => print("getAndSaveUserInfo $value"));
}

Future getAndSaveUserInfo() async {
var token = await getToken();
var userInfo = await getUserInfo(token);
return saveUserInfo(userInfo);
}

Future getToken() {
return Future.delayed(Duration(seconds: 2), () {
return "token";
});
}

Future getUserInfo(String token) {
return Future.delayed(Duration(seconds: 2), () {
return UserInfo();
});
}

Future saveUserInfo(UserInfo info) {
return Future.delayed(Duration(seconds: 1), () {
return true;
});
}
   输出:
getAndSaveUserInfo true

Stream

Stream也是用于接收异步事件数据,与Future不同的是,它可以接收多个异步事件操作结果。换句话说,就是可以同通过多次触发成功或者失败来传递数据结果和失败异常。常用于多次读取数据的异步场景,比如网路下载、文件读取等
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void main() {
Stream.fromFutures([
Future.delayed(Duration(seconds: 1), () {
return "hello 1";
}),
Future.delayed(Duration(seconds: 3), () {
return "hello 2";
}),
Future.delayed(Duration(seconds: 2), () {
return "hello 3";
}),
Future.delayed(Duration(seconds: 4), () {
throw AssertionError("error 4");
})
]).listen((event) {
print("event $event");
}).onError((handleError) {
print("handleError $handleError");
});
}
输出:
event hello 1
event hello 3
event hello 2
handleError Assertion failed: "error 4"