美文网首页程序员
Dart学习笔记08-异步

Dart学习笔记08-异步

作者: zombie | 来源:发表于2020-08-20 20:45 被阅读0次
    Dart是单线程的,Dart基于单线程加事件循环来完成耗时操作的处理,下图为Dart官方提供的Dart运行原理图。 Dart运行原理

    1. Dart的事件循环

    Dart 在单线程中是以消息循环机制来运行的,其中包含两个任务队列,一个是微任务队列microtask queue,另一个叫做事件队列event queue。从图中可以发现,微任务队列的执行优先级高于事件队列。
    如上图中所示,入口函数 main() 执行完后,消息循环机制便启动了。首先会按照先进先出的顺序逐个执行微任务队列中的任务,事件任务执行完毕后程序便会退出,但是,在事件任务执行的过程中也可以插入新的微任务和事件任务,在这种情况下,整个线程的执行过程便是一直在循环,不会退出,而Flutter中,主线程的执行过程正是如此,永不终止。
    在Dart中,所有的外部事件任务都在事件队列中,如IO、计时器、点击、以及绘制事件等,而微任务通常来源于Dart内部,并且微任务非常少,之所以如此,是因为微任务队列优先级高,如果微任务太多,执行时间总和就越久,事件队列任务的延迟也就越久,对于GUI应用来说最直观的表现就是比较卡,所以必须得保证微任务队列不会太长。在事件循环中,当某个任务发生异常并没有被捕获时,程序并不会退出,而直接导致的结果是当前任务的后续代码就不会被执行了,也就是说一个任务中的异常是不会影响其它任务执行的。

    2. Dart的异步操作

    Dart中的异步操作主要使用Future以及asyncawait

    • Future
      模拟网络请求需要3秒钟的时间,之后返回数据,代码如下:
    import "dart:io";
    
    main(List<String> args) {
      print("main function start");
      print(getNetworkData());
      print("main function end");
    }
    
    String getNetworkData() {
      sleep(Duration(seconds: 3));
      return "network data";
    }
    

    打印结果:

    main function start
    // 等待3秒
    network data
    main function end
    

    这段代码getNetworkData会阻塞main函数的执行,Dart中可以使用Future创建了一个实例,进行异步操作,代码如下:

    import "dart:io";
    
    main(List<String> args) {
      print("main function start");
      print(getNetworkData());
      print("main function end");
    }
    
    Future<String> getNetworkData() {
      return Future<String>(() {
        sleep(Duration(seconds: 3));
        return "network data";
      });
    }
    

    打印结果:

    main function start
    Instance of 'Future<String>'
    main function end
    

    打印结果可见这次没有阻塞现象,有了Future之后,通过.then的回调获取请求结果,代码如下:

    main(List<String> args) {
      print("main function start");
      // 使用变量接收getNetworkData返回的future
      var future = getNetworkData();
      // 当future实例有返回结果时,会自动回调then中传入的函数
      // 该函数会被放入到事件循环中,被执行
      future.then((value) {
        print(value);
      });
      print(future);
      print("main function end");
    }
    

    打印结果:

    main function start
    Instance of 'Future<String>'
    main function end
    // 3s后执行下面的代码
    network data
    

    如果调用过程中出现了异常,可以通过.catchError获取异常信息,代码如下:

    import "dart:io";
    
    main(List<String> args) {
      print("main function start");
      var future = getNetworkData();
      future.then((value) {
        print(value);
      }).catchError((error) { // 捕获出现异常时的情况
        print(error);
      });
      print(future);
      print("main function end");
    }
    
    Future<String> getNetworkData() {
      return Future<String>(() {
        sleep(Duration(seconds: 3));
        // 不再返回结果,而是出现异常
        // return "network data";
        throw Exception("网络请求出现错误");
      });
    }
    

    打印结果:

    main function start
    Instance of 'Future<String>'
    main function end
    // 3s后没有拿到结果,但是我们捕获到了异常
    Exception: 网络请求出现错误
    

    Future.delayed(时间, 回调函数)
    在延迟一定时间时执行回调函数,执行完回调函数后会执行then的回调,代码如下:

    main(List<String> args) {
      print("main function start");
    
      Future.delayed(Duration(seconds: 3), () {
        return "3秒后的信息";
      }).then((value) {
        print(value);
      });
    
      print("main function end");
    }
    
    • await、async
      awaitasync可以让我们用同步的代码格式,去实现异步的调用过程,通常一个async的函数会返回一个Future,将下面代码改成异步:
    import "dart:io";
    
    main(List<String> args) {
      print("main function start");
      print(getNetworkData());
      print("main function end");
    }
    
    String getNetworkData() {
      var result = Future.delayed(Duration(seconds: 3), () {
        return "network data";
      });
    
      return  "请求到的数据:" + result;
    }
    

    在Future.delayed函数前加了一个await,会发现代码报错:[图片上传中...(错误信息.png-75d3c9-1597927310305-0)]

    错误信息.png
    所以需要将将getNetworkData函数定义成async函数,只需要在函数的()后面加上async关键字即可,代码如下:
    String getNetworkData() async {
      var result = await Future.delayed(Duration(seconds: 3), () {
        return "network data";
      });
    
      return  "请求到的数据:" + result;
    }
    

    运行代码会依旧报错:

    错误信息.png
    使用async标记的函数,必须返回一个Future对象,继续修改代码:
    main(List<String> args) {
      print("main function start");
      getNetworkData().then((value){
          print(value);
      });
      print(getNetworkData());
      print("main function end");
    }
    
    Future<String> getNetworkData() async {
      var result = await Future.delayed(Duration(seconds: 3), () {
        return 'network data';
      });
    
      return  "请求到的数据:" + result;
    }
    

    ps:返回的时候并不需要包装一个Future,直接返回即可,返回值会默认被包装在一个Future

    • Stream
      Stream也是用于接收异步事件数据,和Future不同的是,它可以接收多个异步操作的结果。Stream常用于会多次读取数据的异步任务场景,如网络内容下载、文件读写等。代码示例:
    Stream.fromFutures([
        // 1s后返回结果
        Future.delayed(Duration(seconds: 1), () {
          return 'task 1';
        }),
    
        // 抛出一个异常
        Future.delayed(Duration(seconds: 2), () {
          throw AssertionError('Error');
        }),
    
        // 3秒后返回结果
        Future.delayed(Duration(seconds: 3), () {
          return 'task 3';
        })
      ]).listen((data) {
        print(data);
      }, onError: (e) {
        print(e.message);
      }, onDone: () {});
    

    输出结果如下:

    task 1
    Error
    task 3
    

    3. 如何创建微任务

    我们可以通过dart中async下的scheduleMicrotask来创建一个微任务,代码示例:

    import "dart:async";
    
    main(List<String> args) {
      scheduleMicrotask(() {
        print("Hello Microtask");
      });
    }
    

    相关文章

      网友评论

        本文标题:Dart学习笔记08-异步

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