前言

因为flutter使用的是dart语言,所以需要对dart作一定的学习
flutter官方文档: 链接教程

基础

变量声明

var

类似JS中的var,可以接受任何类型的变量
最大的不同是Dart中var变量一旦赋值,类型便会确定,不能改变其变量(thanks for inventing Javascript)
因为Dart是强类型语言,而JS是弱类型脚本语言

Object 和 dynamic

Object是所有对象的根基类,所有类都是Object的子类
dynamic与var一样可以赋值任意对象,但是dynamic赋值的变量在赋值后可以后期改变变量类型(Object也可以)

 dynamic t;
 Object x;
 t = "hi world";
 x = 'Hello Object';
 //下面代码没有问题
 t = 1000;
 x = 1000;

dynamic声明的对象编译器会提供所有的组合,而Object声明的对象只能使用Object的属性与方法

final与const

一个final变量只能被设置一次

  • const变量是一个编译时常量
  • final变量在第一次使用时被初始化
  • 被这两种类型修饰的变量,可以忽视类型

函数

  • Dart中函数也是对象,且有一个类型Function
  • 因此函数可以赋值给变量或者作为参数传递给其他函数
  • Dart中的函数如果没有显式声明返回值类型时会默认当成dynamic来处理
  • 函数返回值没有类型推断

对于只包含一个表达式的函数,可以使用简写语法

bool isNoble (int atomicNumber)=> _nobleGases [ atomicNumber ] != null ;

函数作为参数范例(来自官方文档)

void execute(var callback) {
    callback();
}
execute(() => print("xxx"))

可选的位置参数

包含一组函数参数,用[]标记为可选的位置参数,并放在参数列表的最后面
范例(来自官方文档)

String say(String from, String msg, [String device]) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

可选的命名参数

定义函数时,使用{param1, param2, ...}放在参数列表的最后面用于指定命名参数

//设置[bold]和[hidden]标志
void enableFlags({bool bold, bool hidden}) {
    // ... 
}

调用函数时,可以用指定命名参数 paramName : value
enableFlags(bold: true, hidden: false);

不能同时使用可选的位置参数与可选的命名参数

异步支持

Dart类库有非常多的返回Future或者Stream对象的函数,这些函数被称为异步函数
asyncawait关键词支持异步编程

Future

与Javascript中的Promise相似,表示一个异步操作的最终完成(失败)及其结果值的表示
一个Future只会对应一个结果,要么成功要么失败

Future.then

下方Duration函数将会在延时2s之后返回字符串"hi world!"
而 在then中接收异步结果并打印结果

Future.delayed(new Duration(seconds: 2),(){
   return "hi world!";
}).then((data){
   print(data);
});

Future.catchError

倘若异步任务发生错误,可以在catchError中捕获错误

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");  
}).then((data){
   //执行成功会走到这里  
   print("success");
}).catchError((e){
   //执行失败会走到这里  
   print(e);
});

捕获错误还可以直接用then方法中的可选参数 onError

Future.delayed(new Duration(seconds: 2), () {
    //return "hi world!";
    throw AssertionError("Error");
}).then((data) {
    print("success");
}, onError: (e) {
    print(e);
});

Future.whenComplete

就是无论异步失败还是成功,都会执行的内容,比如弹出对话框,除了在两个内容内都加上对应操作之外,
直接添加whenComplete进行回调是一种更省事的方法

Future.delayed(new Duration(seconds: 2),(){
   //return "hi world!";
   throw AssertionError("Error");
}).then((data){
   //执行成功会走到这里 
   print(data);
}).catchError((e){
   //执行失败会走到这里   
   print(e);
}).whenComplete((){
   //无论成功或失败都会走到这里
});

Future.wait

该函数中接受一个Future数组参数,只有数组中所有Future都执行成功后,才会触发then的成功回调,任意一个Future执行失败都会触发错误回调

下列通过两个Future.delayed来模拟两个数据获取的异步任务

Future.wait([
  // 2秒后返回结果  
  Future.delayed(new Duration(seconds: 2), () {
    return "hello";
  }),
  // 4秒后返回结果  
  Future.delayed(new Duration(seconds: 4), () {
    return " world";
  })
]).then((results){
  print(results[0]+results[1]);
}).catchError((e){
  print(e);
});

回调地狱

如果代码中有大量异步逻辑,并且出现大量异步任务依赖其他异步任务的结果时,必然会出现Future.then回调中套回调的情况

过多的嵌套会导致代码可读性下降以及出错率提高,非且非常难维护
这是之前JS被吐槽最多的点,而后期通过

  • ECMAScript6引入Promise
  • ECMAScript7引入async/await

而Dart直接兼顾了这两个解决方法

  • Future相当于Promise
  • async/await还是原来的配方,连名字都一样

使用Future来消除回调地狱

Future的所有返回值都是Future对象,因此可以方便进行链式调用
倘若在then中返回的是一个Future话,该future会执行,执行结束后会触发then回调,依次向下就避免了嵌套

美中不足就是仍然存在一层回调

使用async/await来消除回调地狱

task() async {
   try{
    String id = await login("alice","******");
    String userInfo = await getUserInfo(id);
    await saveUserInfo(userInfo);
    //执行接下来的操作   
   } catch(e){
    //错误处理   
    print(e);   
   }  
}
  • async表示函数是异步的,定义的函数会返回一个Future,可用then方法添加回调函数
  • await后面是一个Future,表示等待该异步任务完成,完成后才会往下走
  • await函数必须在async函数内部

Stream

用于接收异步事件数据,与Future不同的是,它可以接受多个异步操作的结果

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议