美文网首页
Flutter - 异步

Flutter - 异步

作者: 水煮杰尼龟 | 来源:发表于2021-07-18 21:33 被阅读0次
前言
  • 首先Dart是单线程的
  • 开发中肯定会遇到很多耗时操作的,而单线程肯定不能一直在等待这些耗时操作完成,而造成阻塞
  • 那么Dart里是如何处理的呢,答:Dart是基于单线程加事件循环来完成耗时操作的处理的
事件循环
  • 将需要处理的事件,放到一个事件队列中,不断的从队列中取出对应的事件,并执行。

  • 跟咱iOS里的Runloop类似
    引用一下官方图,齿轮就是事件循环,不断的从左边取事件执行

    image.png
  • 实际上Dart从两个队列执行任务:event事件队列和microtask微任务队列;

  • 而微任务队列的优先级要高于事件队列;

  • 所以是优先执行微任务队列中的任务,再执行 事件队列 中的任务

  • 一般来说,I/O事件,手势,Timer,绘制,其他外部事件都会放到event事件队列中,而微任务通常来源于Dart内部,并且很少,因为如果微任务太多,可能会阻塞事件队列,而导致一些用户操作可能就没有反应了。

  • 两个队列都是按照先进先出的顺序执行任务

Dart的异步操作
  • 主要使用Future以及asyncawait
首先先举个栗子,看一下同步的耗时操作
void main() {
  print('start ${DateTime.now()}');
  print('${sendRequest()} ${DateTime.now()}');
  print('end ${DateTime.now()}');
}

String sendRequest(){
  sleep(Duration(seconds: 2));
  return 'Request finish';
}

毫无疑问,sendRequest会阻塞main函数的执行,可以看到打印startRequest finish之间相隔了2秒

image.png
使用Future
void main() {
  print('start ${DateTime.now()}');
  print('${sendRequest()} ${DateTime.now()}');
  print('end ${DateTime.now()}');
}

Future<String> sendRequest(){
  return Future<String>((){
    sleep(Duration(seconds: 2));
    return 'Request finish';
  });
}

你会看到,sendRequest丝毫没有阻塞main函数的执行,直接连续print了3次,显然这才是我们需要的效果,但是打印的是一个Future实例,那么我们需要拿到最终的结果。

image.png
获取Future结果
  • 通过.then的回调
void main() {
  print('start ${DateTime.now()}');
  print('${sendRequest().then((value){
    print('$value ${DateTime.now()}');
  })} ${DateTime.now()}');

  print('end ${DateTime.now()}');
}
image.png
执行异常捕获
void main() {
  print('start ${DateTime.now()}');
  print('${sendRequest().then((value){
    print('$value ${DateTime.now()}');
  }).catchError((error){
    print('$error ${DateTime.now()}');
  })} ${DateTime.now()}');

  print('end ${DateTime.now()}');
}

Future<String> sendRequest(){
  return Future<String>((){
    sleep(Duration(seconds: 2));
    //return 'Request finish';
    throw Exception('出异常了');
  });
}
image.png
Future的链式调用
  • 可以在then中继续return,可以在下一个then中拿到结果
void main() {
  sendRequest().then((value) {
    print('11 - $value ${DateTime.now()}');
    return '海贼·王路飞';
  }).then((value) {
    print('22 - $value ${DateTime.now()}');
    return '三刀·流索隆';
  }).then((value) {
    print('33 - $value ${DateTime.now()}');
  });
}

Future<String> sendRequest(){
  return Future<String>((){
    sleep(Duration(seconds: 2));
    return 'Request finish';
  });
}
image.png
其他
  • Future.value:返回一个指定值的Future
void main() {
  print('start');
  Future.value('王路飞').then((value) {
    print('$value');
  });
  print('end');
}
image.png
  • Future.error:返回一个异常的 Future
void main() {
  print('start');
  Future.error(Exception('异常了')).then((value) {
    print('$value');
  });
  print('end');
}
image.png
  • Future.delayed:延迟执行
void main() {
  print('start ${DateTime.now()}');
  Future.delayed(Duration(seconds: 2)).then((value) {
    print('delayed ${DateTime.now()}');
  });
  print('end ${DateTime.now()}');
}
image.png
当然还有很多api,可以自行了解
await、async
  • Dart中的关键字,async声明函数内部有代码需延迟执行,await声明运算为延迟执行
  • async的函数需要返回一个Futureawait需在async函数中使用
  • 实际上是Dart异步编程用于简化异步API操作的,能够将异步的代码使用同步的代码结构实现
    举个栗子,如下,sleepTime的结果,我在sendRequest里需要拼接一个a给出去,可能如下写法
void main() {

  print('start ');
  print(sendRequest().then((value) {
    print(value);
  }));

  print('end ');
}

Future sendRequest() {

  final a = ' 12345';
  
  return sleepTime().then((value) {
    return value+a;
  });

}
///假设异步耗时请求
Future sleepTime(){
  return Future((){
    sleep(Duration(seconds: 2));
    return 'request finish';
  });
}

如果用await,async来写 看看

void main() {

  print('start ');
  print(sendRequest().then((value) {
    print(value);
  }));

  print('end ');
}

Future sendRequest() async{

  final a = ' 12345';
  var result = await sleepTime();
  
  return result + a;

}
///假设异步耗时请求
Future sleepTime(){
  return Future((){
    sleep(Duration(seconds: 2));
    return 'request finish';
  });
}
  • 这样可以像写同步代码一样去使用Future异步返回的结果
任务执行顺序
  • 顺序: main函数 -> 微任务队列(Microtask Queue)-> 事件队列(Event Queue)
  • 可以通过scheduleMicrotask创建一个微任务,Future是加到事件队列
  • 那么来看看执行顺序
void main() {

  Future(() => print("event task"));
  print('start ');
  
  scheduleMicrotask((){
    print('scheduleMicrotask');
  });
  print('end ');
}
image.png
  • 可以看到main函数里的 startend先打印了,然后打印了微任务scheduleMicrotask,最后打印了event task
    Future代码的加入队列

Future代码分为构造函数的函数体和(.then/.catchError等)

  • Future构造函数的函数体是放在事件队列中
  • .then的函数体分成几种情况
  1. .then函数体会跟着构造函数体放进事件队列
  2. Future链式调用,.then函数体会依次放入事件队列
  3. Future里或者.then里使用微任务,微任务会在Future完成后才调用
    来分析一个复杂的情况,彻底搞清楚任务执行顺序
void main() {
  print("main start");

  Future(() => print("task1"));

  final future = Future(() => null);

  Future(() => print("task2")).then((_) {
    print("task3");
    scheduleMicrotask(() => print('task4'));
  }).then((_) => print("task5"));

  future.then((_) {
    print('task6');
    Future(() => print('task7')).then((value) {
      Future(() => print('task8'));
    });
  }).then((value) {
    print('task9');
  });
  scheduleMicrotask(() => print('task10'));

  Future(() => print('task11')).then((_) {
    Future(() => print('task12')).then((value) {
      Future(() => print('task13'));
    });
  }).then((_) => print('task14'));

  Future(() => print('task15'));

  print("main end");
}
image.png

用下面的图,捋了一下。


image.png

从图中可以梳理出打印顺序为:

- `main start`
- `main end`
- `task10`
- `task1`
- `task6`
- `task9`
- `task2`
- `task3`
- `task5`
- `task4`
- `task11`
- `task14`
- `task15`
- `task7`
- `task12`
- `task8`
- `task13`

与实际打印结果一致

isolate
  • 我们知道Dart是单线程的,在Dart中其实有个专门名词叫isolate(隔离)
  • Flutter中就有一个Root isolate, 在Root isolate内部运行一个EventLoop事件循环
  • 用官方的话来说 isolate就是一个隔离的Dart执行上下文
  • Dart是支持多个isolate的,isolate之间不共享任何资源,只能依靠消息机制通信
  • 如果确实有非常耗时的操作,可以自己创建isolate
创建isolate
void main() {

  print(Isolate.current.hashCode);
  Isolate.spawn(testIsolate, "new Isolate");
}
void testIsolate(String msg) {
  print("新的isolate:$msg - ${Isolate.current.hashCode}");
}

通信
void main() {

  print(Isolate.current.hashCode);
  // 创建管道
  ReceivePort receivePort= ReceivePort();

  Future iso = Isolate.spawn(testIsolate, receivePort.sendPort,debugName: '哈哈哈');
  iso.then((value) {
    receivePort.listen((data) {
      print('receiveData:$data');
      //关闭管道
      receivePort.close();
      // 杀死isolate
      value.kill(priority: Isolate.immediate);
    });
  });
}
void testIsolate(SendPort port) {
  print("新的isolate - ${Isolate.current.debugName}  ${Isolate.current.hashCode}");
  port.send("Hello World");
}
image.png
compute(Flutter对Isolate的封装,需要在Flutter中使用)
@override
  Widget build(BuildContext context) {
    testCompute();
    return MaterialApp(
        home: Container(),
    );
  }
  void testCompute() async {
    String result = await compute(sendMesg, '哈哈哈');
    print('$result');
  }
  static String sendMesg(String msg) {
    print(msg);
    return '收到了- 回你'+msg;
  }

相关文章

  • Flutter 学习之路 - 异步任务

    实验 Flutter 的异步任务 --- 代码Github地址 Fultter 异步任务试验 Flutter 在很...

  • Flutter 异步

    业务流程:请求用户个人信息,获取到用户个人信息后,为了使用方便,我们需要将其缓存在本地 Future login...

  • Flutter - 异步

    前言 首先Dart是单线程的 开发中肯定会遇到很多耗时操作的,而单线程肯定不能一直在等待这些耗时操作完成,而造成阻...

  • Flutter异步

    多个任务完成后执行下一个任务Future printUserName() async {List r...

  • 2019-09-23: 十一:Flutter之Dart异步操作?

    十一:Flutter之Dart异步操作? 1: Dart 的异步模型?我们先搞清楚dart是如何搞定异步操作的?1...

  • flutter之---Future的正确用法

    在flutter中经常会用到异步任务,dart中异步任务异步处理是用Future来处理,那么如何实现用Future...

  • flutter Future

    在flutter中经常会用到异步任务,dart中异步任务异步处理是用Future来处理,那么如何实现用Future...

  • Dart中的异步支持

    文章参考:《Flutter实战》 使用async和await关键字编写异步程序async :用来表示函数是异步的,...

  • flutter 异步浅析

    以下内容从官网得到:https://webdev.dartlang.org/articles/performanc...

  • Flutter 异步编程

    Dart 和 JavaScript 共同点是——单线程,同步代码会阻塞程序。 因此程序里有大量的异步操作,它是用F...

网友评论

      本文标题:Flutter - 异步

      本文链接:https://www.haomeiwen.com/subject/yiywpltx.html