Flutter - Dart之异步编程

1,208 阅读4分钟

日常开发中:网络数据的获取、大文件本地数据库的写入等耗时等待操作都会涉及到异步编程。与iOS多线程不同的是,Dart是一门单线程语言。当然Dart是一门单线程语言,同样也支持了异步/多线程编程

Dart的事件循环(event loop)

在Dart中,实际上有两种队列:

  1. 事件队列(event queue),包含所有的外来事件:I/O、mouse events、drawing events、timers、isolate之间的信息传递。

  2. 微任务队列(microtask queue),表示一个短时间内就会完成的异步任务。它的优先级最高,高于event queue,只要队列中还有任务,就可以一直霸占着事件循环。microtask queue添加的任务主要是由 Dart内部产生。

在每一次事件循环中,Dart总是先去第一个microtask queue中查询是否有可执行的任务,如果没有,才会处理后续的event queue的流程。

异步任务我们用的最多的还是优先级更低的 event queue。Dart为 event queue 的任务建立提供了一层封装,就是我们在Dart中经常用到的Future。

正常情况下,一个 Future 异步任务的执行是相对简单的:

  1. 声明一个 Future 时,Dart 会将异步任务的函数执行体放入event queue,然后立即返回,后续的代码继续同步执行。
  2. 当同步执行的代码执行完毕后,event queue会按照加入event queue的顺序(即声明顺序),依次取出事件,最后同步执行 Future 的函数体及后续的操作。

Future

Future 类,其表示一个 T 类型的异步操作结果。如果异步操作不需要结果,则类型为 Future。也就是说首先Future是个泛型类,可以指定类型。如果没有指定相应类型的话,则Future会在执行动态的推导类型

Future用法

1. Future+async

String _name = "dingding";
void main() {
  print("开始_name = ${_name}");
  getData_async_future();
  print("结束_name = ${_name}");
}

//async 标记是一个异步函数
void getData_async_future() async {
  Future future = Future(() {
    //Futuren内部放耗时操作
    for (int i = 0; i < 10000; i++) {
      _name = "lala";
    }
    print("耗时操作");
    return _name;
    print("耗时结束_name = ${_name}");
  });
  //使用then接收Future 中的回调
  future.then((value) {
    print(value);
    print("耗时结束_name = ${_name}");
  });
}

打印

flutter: 开始_name = dingding
flutter: 结束_name = dingding
flutter: 耗时操作
flutter: lala
flutter: 耗时结束_name = lala

可以看到结束操作早于耗时操作,说明异步处理了耗时操作。

2. async 搭配await

await:后面跟着一个Future,表示等待该异步任务完成,异步任务完成后才会继续往下执行。await只能出现在异步函数内部。能够让我们可以像写同步代码那样来执行异步任务而不使用回调的方式。

注意点

  1. 后面的操作必须是异步才能用awati
  2. 当前函数必须是异步函数
void getData_async_awati() async {
  
  print("start");
  Future future = await Future(() {
    for (int i = 0; i < 10000; i++) {
    }
    print('耗时操作');
  });
  print("end");
}

打印

flutter: start
flutter: 耗时操作
flutter: end

3. then、error、whenComplete 链式语法

  1. then:在Future内部函数处理完后就会有回调
  2. error:在Future内部抛出错误时,会有回调
  3. whenComplete:不管Future内部是否抛出错误,都会有回调 eg: 写法一:直接在then函数中取onError函数回调
void getData_async_future_error() async {
  //链式语法推荐
  Future(() {
    for (int i = 0; i < 10000; i++) {
    }
    throw Exception("网络异常");
  }).then((value) {
    print('then 来了');
    print(value);
  },onError: (error) {
    print('捕获了错误 error:${error.toString()}');
  }).whenComplete(() => print('完成了'));
  
  //非链式语法 不推荐
  Future future = Future(() {
    for (int i = 0; i < 10000; i++) {
    }
    throw Exception("网络异常");
  });
  future.then((value) {
    print('then 来了');
    print(value);
  },onError: (error) {
    print('捕获了错误 error:${error.toString()}');
  });
  future.whenComplete(() => print('完成了'));
}

打印:

flutter: 捕获了错误 error:Exception: 网络异常
flutter: 完成了

上面的then、whenComplete都是用链式语法,推荐使用这中方式

方式二:使用catchError

  Future(() {
    for (int i = 0; i < 10000; i++) {
    }
    throw Exception("网络异常");
  }).then((value) {
    print('then 来了');
    print(value);
  }).catchError((error) {//处理错误
    print('捕获了错误 error:${error.toString()}');
  }).whenComplete(() => print('完成了'));

4. future执行顺序

void futureOrder() {
  //执行顺序按照编写顺序,类似队列
  //then比Future优先级高
  Future(() {
    sleep(Duration(seconds: 1));
    return '任务一';
  }).then((value) {
    print('$value 结束');
    return "任务五";
  }).then((value) => print(value));

  Future(() {
    return '任务二';
  }).then((value) => print('$value 结束'));

  Future(() {
    return '任务三';
  }).then((value) => print('$value 结束'));

  Future(() {
    return '任务四';
  }).then((value) => print('$value 结束'));

  print('添加完毕');
}

打印:

flutter: 开始_name = dingding
flutter: 添加完毕
flutter: 结束_name = dingding
flutter: 任务一 结束
flutter: 任务五
flutter: 任务二 结束
flutter: 任务三 结束
flutter: 任务四 结束

5. 依赖

void futureDepandense() {
  Future.wait([
    Future(() {
      print('任务一');
      return "任务一";
    }),
    Future(() {
      sleep(Duration(seconds: 1));
      print('任务二');
      return "任务二";
    }),
  ]).then((value) {//注意此时value 就是一个数组
    print('then:来了:${value[0]} + ${value[1]}');
    print('任务3');
  });
}

打印:

flutter: 任务一
flutter: 任务二
flutter: then:来了:任务一 + 任务一
flutter: 任务3

6. scheduleMicrotask

  1. scheduleMicrotask 比Future异步任务高
  2. Future 在事件队列里面
  3. 在同级中,then 优先级 < scheduleMicrotask
  4. 在then函数中,then 优先级 > scheduleMicrotask
void dartLoop() {
  print('out task 1');
  Future(() => print("A")).then((value) => print("A 结束"));
  Future(() => print("B")).then((value) => print("B 结束"));

  scheduleMicrotask(() {
    print('Microtask 1');
  });

  print('out task 2');
}

打印:

flutter: out task 1
flutter: out task 2
flutter: Microtask 1
flutter: A
flutter: A 结束
flutter: B
flutter: B 结束

多线程

Dart中一般使用Isolate实现多线程。Isolate有独立的内存空间,不用考虑多线程时资源抢夺问题,即不需要锁。所以Isolate更像多进程操作。但是进程之间的通信也就相对麻烦,其使用port通信。

int a = 10;
Future<void> dartIsolate() async {
  print('开始');
  print("外部 a = ${a}");
  Isolate.spawn(funcA,100);

  //创建Port
  ReceivePort port = ReceivePort();
  //创建Isolate
  Isolate ISO = await Isolate.spawn(funcB,port.sendPort);
  port.listen((message) {
    print("portMessage ${message}");
    a = message;
    //关闭端口
    port.close();
    //销毁ISO
    ISO.kill();
  });
  print('结束');
  print("外部 a = ${a}");
}

void funcA(int p) {
  a = p;
  print('funcA');
  print("内部 a = ${a}");//a = 100 说明两个a不在同一片内存空间,间接说明 Isolate其实是开辟一个新的进程
}

void funcB(SendPort sendPort) {
  print('funcB');
  sendPort.send(1000);
}

打印:

flutter: 开始_name = dingding
flutter: 开始
flutter: 外部 a = 10
flutter: 结束_name = dingding
flutter: funcA
flutter: 内部 a = 100
flutter: funcB
flutter: 结束
flutter: 外部 a = 10
flutter: portMessage 1000

更轻的compute

compute 是对Isolate的封装,更轻量的。线程通信更加方便

//compute 是对Isolate的封装,更轻量的。线程通信更加方便
void computeTest() async {
  print("start");
  //compute能直接拿到回调,不需要做多余的进程端口通信
  int x = await compute(funcC,10);
  print('x = ${x}');
  print("end");
}

int funcC(int p) {
  print('funcC');
  return p + 100;
}

打印:

flutter: start
flutter: funcC
flutter: x = 110
flutter: end

面试题

下面打印顺序是什么

void dartLoopTest() {
  Future x0 = Future(() => null);
  Future x = Future(() => print('1'));
  Future(() => print('2'));
  scheduleMicrotask(() => print('3'));
  x.then((value) {
    print('4');
    Future(() => print('5'));
  }).then((value) => print('6'));
  print('7');
  x0.then((value) {
    print('8');
    scheduleMicrotask(() {
      print('9');
    });
  }).then((value) => print('10'));
}

打印:

flutter: 7
flutter: 3
flutter: 8
flutter: 10
flutter: 9
flutter: 1
flutter: 4
flutter: 6
flutter: 2
flutter: 5

参考 Dart中的异步编程——Future、async和await