Dart(三)泛型、异步知多少

Dart(三)泛型、异步知多少

泛型

eg. List<E>Map<E>Set<E>

通常情况下,会使用一个字母来代表类型参数, 例如 E, T, S, K, 和 V 等。

为什么使用泛型?
  1. 提高代码质量、提高代码可读性
  2. 减少重复代码
  3. 保证类型安全

比如下面两段代码是否可以编译通过?

class Demo1 {
  void func1() {
    List<String> stringList = [];
    stringList.add(1);
  }
}
class Demo1 {
  void func2() {
    List stringList = [];
    stringList.add(1);
    stringList.add('value');
  }
}
//弊端?

image-20210926102534660

泛型的使用
在集合中使用泛型
  1. 上述方式,直接通过指定类型声明集合类型

  2. List , Set 和 Map 字面量也是可以参数化的。 参数化字面量和之前的字面量定义类似, 对于 List 或 Set 只需要在声明语句前加 <*type*> 前缀, 对于 Map 只需要在声明语句前加 <*keyType*, *valueType*> 前缀

    var List = <String>['tt'];
    var Set = <String>{'tt'};
    var Map = <String, String> {
      'key': 'tt',
    };
    
  3. 通过集合的构造函数指定类型

    List为例

    var list3 = List<String>.of(['TT', 'OO']);
    

    image-20210926105946039

在类中使用泛型
class Demo3<T> {
  late T variate;

  T print(T param) {
    // ignore: unused_local_variable
    T innerParam;

    var param1;
    return param1 as T;
  }
}
泛型方法
class Demo4 {
  List<E> getData<E>(E element) {
    return [];
  }

  R getElement<R>() {
    return null as R;
  }
}

在类中,哪里可以使用范型

  1. 成员变量
  2. 函数的参数列表、返回值
  3. 局部变量
  • 使用extends关键字,限制泛型范围
class Demo4<T extends TypeClass> {
}

使用:

class TypeClass {}

class TypeClassChild extends TypeClass {}

class TypeClass1 {}

main() {
  var demo1 = Demo3<TypeClass>();
  Demo3<TypeClass1> demo2 = Demo3();
  
  var demo3 = Demo4<TypeClass>();
  // var demo4 = Demo4<TypeClass1>(); //compile error;
  var demo5 = Demo4<TypeClassChild>();
}

image-20210926113018362

思考:运行时,如何判断集合的类型?

Dart中泛型类型是 固化的,也就是说它们在运行时是携带着类型信息的。

Java中的泛型会被 擦除 ,也就是说在运行时泛型类型参数的信息是不存在的。 在Java中,可以测试对象是否为 List 类型, 但无法测试它是否为 List<String>

image-20210928145645819

异步

概念
  • 同步

简单说,就是方法调用时按照代码编写的顺序,从上到下依次调用执行,比较常见的调用方式。

  • 异步

为了解决同步调用耗时操作阻塞的问题,两条腿或多条腿走路。

  • 异步实现方式

    • 多线程
      • 可以并发执行,解决耗时问题
      • 耗费系统资源
      • 并发锁问题,产生死锁
    • 异步模型

    简单说就是在某个单线程中存在一个事件循环和一个事件队列。

    事件循环不断的从事件队列中取出事件来执行,这里的事件就好比是一段代码,每当遇到耗时的事件时,事件循环不会停下来等待结果,它会跳过耗时事件,继续执行其后的事件。当不耗时的事件都完成了,再来查看耗时事件的结果。因此,耗时事件不会阻塞整个事件循环,这让它后面的事件也会有机会得到执行。

Dart中的异步Event Loop处理机制

Dart是单线程模型,是事件驱动体系结构,该结构基于单个事件循环和两个事件队列的单线程执行模型。

单线程事件处理机制

Dart中的两个事件队列
  • 微任务队列(MicroTask Queue
    • 通常有时候事件处理想要在稍后完成一些任务但又希望是在执行下一个事件消息之前。
  • 事件队列(Event Queue
    • 包含所有外来的事件:I/O,mouse events,drawing events,timers,isolate之间的message等。

event队列包含Dart和来自系统其它位置的事件。

但microtask队列只包含来自当前isolate的内部代码。

Dart中的事件循环机制

  1. 先查看MicroTask队列是否为空,不是则先执行MicroTask队列
  2. 一个MicroTask执行完后,检查有没有下一个MicroTask,直到MicroTask队列为空,才去执行Event队列
  3. Event队列取出一个事件处理完后,再次返回第一步,去检查MicroTask队列是否为空

MicroTask队列优先级高于Event队列

怎样向MicroTask队列和Event队列添加事件
import 'dart:async';

  • MicroTask队列添加事件

    • 使用**scheduleMicrotask**方法添加

      	scheduleMicrotask(() {
          print('this is microtask2');
        });
      
    • 使用**Future**对象添加

      	Future.microtask(() {
          print('this is microtask1');
        });
      
      ///
      /// microtask的实现
      	factory Future.microtask(FutureOr<T> computation()) {
          _Future<T> result = new _Future<T>();
          scheduleMicrotask(() {
            try {
              result._complete(computation());
            } catch (e, s) {
              _completeWithErrorCallback(result, e, s);
            }
          });
          return result;
        }
      
  • Event队列添加事件

    • 使用**Future**对象添加

        Future(() {
          print('this is my task1');
        });
      

我们可以认为***Dart 为 Event Queue 的任务建立提供了一层封装,叫作 Future。***通过Future可以对事件结果进行操作。

异步之Future

什么是Future,顾名思义,表示一件将来会发生的事情(也就是不会立即执行),将来可以从Future中取到一个,当一个方法返回一个Future时,发生两件事:

  1. 这个方法将某件事情排队,返回一个未完成的Future
  2. 这个方法事情完毕后,Future的状态会变成已经完成,这个时候可以取到这件事情的返回值。

Future表示一个异步操作的最终完成(或失败)及其结果值的表示,简单来说,它就是用来处理异步操作的,异步处理成功就执行成功的操作,异步处理失败就捕获错误或者停止后续操作,一个Future只会对应一个结果,要么成功,要么失败。

创建Future

image-20210927101000697

值得注意的一点

延时任务Future.delayed中的duration延时事件真的靠谱吗?

分析如下代码输出结果:(future.delay.dart)

import 'dart:async';
import 'dart:io';

void main() {
  print("main start");

  Future.delayed(Duration(seconds: 1), () {
    print('task delayed');
  });

  Future(() {
    // 模拟耗时5秒
    sleep(Duration(seconds: 5));
    print("5s task");
  });

  print("main stop");
}

///
/// 输出结果
main start
main stop
5s task
task delayed
Exited

结论:

  • Future.delayed是何时把事件加入到Event队列的?

    • 延时时间到了之后

        factory Future.delayed(Duration duration, [FutureOr<T> computation()?]) {
          if (computation == null && !typeAcceptsNull<T>()) {
            throw ArgumentError.value(
                null, "computation", "The type parameter is not nullable");
          }
          _Future<T> result = new _Future<T>();
          new Timer(duration, () {
            if (computation == null) {
              result._complete(null as T);
            } else {
              try {
                result._complete(computation());
              } catch (e, s) {
                _completeWithErrorCallback(result, e, s);
              }
            }
          });
          return result;
        }
      
  • 延时事件前边如果有耗时事件执行的话,只有等耗时任务执行完毕,它才会被执行。

Future的基本操作

then

Future中的任务完成后,我们往往需要一个回调,这个回调立即执行,不会被添加到事件队列。

catchError

用来捕获任务异常。

whenComplete

无论任务成功失败,都会回调。

wait

有时候,需要等待多个异步任务都执行结束后才进行一些操作,如有一个界面,需要从两个接口获取数据,获取成功后,将两个数据进行处理后显示在UI界面上,这时候,Future.wait派出用上了,它接收一个Future数组参数,只有数组中所有的Future执行成功后,就会触发then回调,当然,只要有一个Future执行失败就会触发错误回调。

main(List<String> args) {
  Future.wait([
    Future.value("1.wait"),
    Future.delayed(Duration(seconds: 2), () {
      return "2.TT";
    }),
    //3秒后返回结果
    Future.delayed(Duration(seconds: 3), () {
      return "3.Android";
    }),
    //4秒后返回结果
    Future.delayed(Duration(seconds: 4), () {
      return "4.Future";
    })
  ]).then((data) {
    //成功逻辑
    print(data);
  }).catchError((e) {
    //捕捉错误
    print(e);
  }).whenComplete(() {
    print('complete~~');
  });
}

[1.wait, 2.TT, 3.Android, 4.Future]
complete~~
Exited

any

wait相对,接收参数也是一组Future,表示只要有一个Future完成,则表示成功。

async / await

Dart1.9中加入了asyncawait关键字,有了这两个关键字,我们可以更简洁的编写异步代码,而不需要调用Future相关的API。他们允许你像写同步代码一样写异步代码和不需要使用Future接口。

它仅仅是一个语法糖,简化Future API的使用。

async:标记某个方法为异步方法(耗时方法),在声明方法的时候使用。

await:等待某个异步方法<执行完毕>。是“等待”,所以要在调用耗时方法的时候使用。(虽然是等待,但它不是阻塞的。)

分析如下代码输出结果:(future.async.await.dart)

import 'dart:async';

main() {
  print("main开始");
  _startMethodA();
  print("main结束");
}

_startMethodA() async {
  print("A执行---begin---");
  print(await _startMethodB());
  print("A执行---ennnd---");
}

Future<String> _startMethodB() async {
  print("B执行---begin---");
  String value = await _getData();
  return "B执行---ennnd--- 请求到的数据:$value";
}

Future _getData() async {
  return Future.delayed(Duration(seconds: 2), () => 'Success, I am B~~~');
}

///
/// 运行结果?
规则、结论
  1. Future中的then方法,而只是一个普通的Function Call,在Future执行完后,立即开始同步执行

  2. Futurethen函数之前已经执行完成,则会创建一个task,将该task的添加到microtask queue中,并且该task将会执行通过then传入的函数

  3. thenFuture函数体共用一个事件循环,如果Future有多个then,它们也会按照链式调用的先后顺序同步执行,同样也会共用一个事件循环

  4. 使用Future.value构造函数的时候,就会和第二条一样,创建Task丢到microtask Queue中执行then传入的函数

  5. Future.sync构造函数表示同步执行,执行了它传入的函数之后如果还有then的话,也会立即创建Task丢到microtask Queue中执行

    then会在 Future 函数体执行完毕后立刻执行,无论是共用同一个事件循环还是进入下一个微任务。

分析如下代码输出结果:(future.example1.dart/future.example2.dart)

import 'dart:async';

main() {
  //普通打印
  print('main #1 of 2');

  //创建一个微任务
  scheduleMicrotask(() => print('microtask #1 of 3'));

  //创建一个延迟Future
  Future.delayed(Duration(seconds: 1), () => print('future #1 (delayed)'));

  //创建一个Future,then链式调用,第二个then中启动一个微任务
  Future(() => print('future #2 of 4'))
      .then((_) => print('future #2a'))
      .then((_) {
    print('future #2b');
    scheduleMicrotask(() => print('microtask #0 (from future #2b)'));
  }).then((_) => print('future #2c'));

  //创建一个微任务
  scheduleMicrotask(() => print('microtask #2 of 3'));

  //创建一个Future,then链式调用,第一个then中返回一个Future
  Future(() => print('future #3 of 4'))
      .then((_) => Future(() => print('future #3a (a future)')))
      .then((_) => print('future #3b'));

  //创建一个Future
  Future(() => print('future #4 of 4'));

  //创建一个微任务
  scheduleMicrotask(() => print('microtask #3 of 3'));

  //普通打印
  print('main #2 of 2');
}

///
/// 输出结果自己思考
import 'dart:async';

main() {
  // 声明一个匿名 Future
  Future(() => print('f1'));

  // 声明 Future fx,其执行体为 null
  Future fx = Future(() => null);

  // 声明一个匿名 Future,并注册了两个 then。在第一个 then 回调里启动了一个微任务
  Future(() => print('f2')).then((_) {
    print('f3');

    scheduleMicrotask(() => print('f4'));
  }).then((_) => print('f5'));

  // 声明了一个匿名 Future,并注册了两个 then。第一个 then 是一个 Future
  Future(() => print('f6'))
      .then((_) => Future(() => print('f7')))
      .then((_) => print('f8'));

  // 声明了一个匿名 Future
  Future(() => print('f9'));

  // 往执行体为 null 的 fx 注册了了一个 then
  fx.then((_) => print('f10'));

  // 启动一个微任务
  scheduleMicrotask(() => print('f11'));

  // 普通输出
  print('f12');

  // 声明一个同步的匿名 Future
  Future.sync(() => print('f14')).then((value) => print('f15'));

  // 声明一个匿名 Future
  Future.value('f13').then((value) => print(value));
}

///
/// 输出结果自己思考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值